diff --git a/.gitignore b/.gitignore index 7cf6671f5..4b847bd0d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ Thumbs.db [Bb]in [Dd]ebug/ [Dd]ebug.win32/ +[Dd]ebug.DLL/ *.sbr *.sdf obj/ diff --git a/.travis.yml b/.travis.yml index ec4a8ebd7..c27b6582c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -120,7 +120,6 @@ script: -DBUILD_EXAMPLES=ON -DBUILD_GAMES=ON -DUSE_EXTERNAL_GLFW=$USE_EXTERNAL_GLFW -DUSE_WAYLAND=$WAYLAND - -DUSE_OPENAL_BACKEND=$OPENAL -DINCLUDE_EVERYTHING=ON .. - $RUNNER make VERBOSE=1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd6ae03ba..94688ea0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Use your best judgement, and feel free to propose changes to this document in a ### raylib philosophy - - raylib is a tool to LEARN videogames programming, every single function in raylib should be a tutorial on itself. + - raylib is a tool to enjoy videogames programming, every single function in raylib should be a tutorial on itself. - raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or is not clearly useful, better not to include it. - raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free. - raylib is collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated. @@ -59,15 +59,23 @@ To open new issue for raylib (bug, enhancement, discussion...), just try to foll Some people ported raylib to other languages in form of bindings or wrappers to the library, here is a list with the ones I'm aware of: - - [raylib C/C++ version (default)](https://github.com/raysan5/raylib) - - [raylib Lua binding](https://github.com/raysan5/raylib-lua) - - [raylib Go binding](https://github.com/gen2brain/raylib-go) - - [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 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](https://github.com/raysan5/raylib) : raylib **C/C++** version (default) + - [raylib-lua](https://github.com/raysan5/raylib-lua) : raylib **Lua** binding + - [raylib-go](https://github.com/gen2brain/raylib-go) : raylib **Go** binding + - [raylib-Nim](https://gitlab.com/define-private-public/raylib-Nim) : raylib **Nim** binding + - [cray](https://gitlab.com/Zatherz/cray) - raylib **Crystal** binding + - [Graphics::Raylib](https://metacpan.org/pod/Graphics::Raylib) : raylib **Perl** wrapper + - [raylib-pascal](https://github.com/drezgames/raylib-pascal) - raylib **Pascal** binding + - [Raylib-cs](https://github.com/ChrisDill/Raylib-cs) : raylib **C#** binding + - [RaylibSharp](https://github.com/TheLumaio/RaylibSharp) : raylib **C#** binding + - [raylib-ruby-ffi](https://github.com/D3nX/raylib-ruby-ffi) : raylib **Ruby** binding + - [raylib-rs](https://github.com/deltaphc/raylib-rs) : raylib **Rust** binding + - [raylib-rust](https://github.com/dtcristo/raylib-rust) : raylib **Rust** binding + - [raylib-py](https://github.com/overdev/raylib-py) : raylib **Python** binding + - [raylib-haskell](https://github.com/DevJac/raylib-haskell) : raylib **Haskell** binding + - [raylib-java]() : raylib **Java** binding + - *[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)* Usually, raylib bindings follow the convention: `raylib-{language}` @@ -79,7 +87,7 @@ provide the icon/logo for that new language binding. If you have any doubt, don't hesitate to [contact me](mailto:ray@raylib.com)!. You can write me a direct mail but you can also contact me on the following networks: - - [raylib forum](http://forum.raylib.com/) - A good place for discussions or to ask for help. + - [raylib reddit](https://www.reddit.com/r/raylib/) - A good place for discussions or to ask for help. - [raylib Discord](https://discord.gg/VkzNHUE) - A direct communication channel for project discussions. - [raylib twitter](https://twitter.com/raysan5) - My personal twitter account, I usually post about raylib, you can send me PMs. - [raylib web](http://www.raylib.com/) - On top-right corner there is a bunch of networks where you can find me. diff --git a/HELPME.md b/HELPME.md index 8ec0edb55..5d9026493 100644 --- a/HELPME.md +++ b/HELPME.md @@ -18,7 +18,7 @@ with a small [donation](http://www.raylib.com/helpme.html) or contributing with raylib philosophy ------------------ - * raylib is a tool to LEARN videogames programming, every single function in raylib should be a tutorial on itself. + * raylib is a tool to enjoy videogames programming, every single function in raylib should be a tutorial on itself. * raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or has not a clear usefulness, better not to include it. * raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free. * raylib is collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated. diff --git a/LICENSE.md b/LICENSE.md index d317c2f3e..b66f47832 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -7,7 +7,7 @@ source code raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software: -Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) +Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. @@ -41,9 +41,9 @@ The following fonts [provided with raylib](https://github.com/raysan5/raylib/tre 2d art ------ -[scarfy spritesheet](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/scarfy.png) and [fudesumi image](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/fudesumi.png) have been created by [Eiden Marsal](https://www.artstation.com/artist/marshall_z) and licensed as [Creative Commons Attribution-NonCommercial 3.0](https://creativecommons.org/licenses/by-nc/3.0/legalcode) +[scarfy spritesheet](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/scarfy.png) and [fudesumi image](https://github.com/raysan5/raylib/blob/master/examples/textures/resources/fudesumi.png) have been created by [Eiden Marsal](https://www.artstation.com/artist/marshall_z) and are licensed as [Creative Commons Attribution-NonCommercial 3.0](https://creativecommons.org/licenses/by-nc/3.0/legalcode) 3d models --------- -[medieval city 3d models and textures](https://github.com/raysan5/raylib/tree/master/examples/models/resources/medieval) have been created by Alberto Cano and licensed as [Creative Commons Attribution-NonCommercial 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode) +[medieval city 3d models and textures](https://github.com/raysan5/raylib/tree/master/examples/models/resources/medieval) have been created by Alberto Cano and are licensed as [Creative Commons Attribution-NonCommercial 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode) diff --git a/README.md b/README.md index d940be4da..7dbaaa6b5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -**raylib is a simple and easy-to-use library to learn videogames programming.** +**raylib is a simple and easy-to-use library to enjoy videogames programming.** raylib is highly inspired by Borland BGI graphics lib and by XNA framework. raylib could be useful for prototyping, tools development, graphic applications, embedded systems and education. -NOTE for ADVENTURERS: raylib is a programming library to learn videogames programming; +NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no auto-debugging... just coding in the most pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html) @@ -22,7 +22,7 @@ features - Multiple platforms supported: **Windows, Linux, MacOS, Android... and many more!** - Written in plain C code (C99) in PascalCase/camelCase notation - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3 or ES 2.0**) - - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.c) + - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - Multiple Fonts formats supported (XNA fonts, AngelCode fonts, TTF) - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) - **Full 3d support** for 3d Shapes, Models, Billboards, Heightmaps and more! @@ -31,7 +31,7 @@ features - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Bindings to **Lua** ([raylib-lua](https://github.com/raysan5/raylib-lua)), **Go** ([raylib-go](https://github.com/gen2brain/raylib-go)) and more! + - Bindings to **Lua** ([raylib-lua](https://github.com/raysan5/raylib-lua)), **Go** ([raylib-go](https://github.com/gen2brain/raylib-go)) and [more](https://github.com/raysan5/raylib/blob/master/CONTRIBUTING.md#raylib-bindings)! raylib uses on its [core](https://github.com/raysan5/raylib/blob/master/src/core.c) module the outstanding [GLFW3](http://www.glfw.org/) library, embedded inside raylib in the form of [rglfw](https://github.com/raysan5/raylib/blob/master/src/rglfw.c) module, avoiding that way external dependencies. diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 0dcf8c70f..88b0a6109 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -5,8 +5,7 @@ * This example has been created using raylib 1.8 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Example based on Berni work on Raspberry Pi: -* http://forum.raylib.com/index.php?p=/discussion/124/line-versus-triangle-drawing-order +* Example based on Berni work on Raspberry Pi. * * Copyright (c) 2017 Ramon Santamaria (@raysan5) * diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h index 24ed3686a..694e3a958 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; + Matrix transform; // Local transform matrix + + int meshCount; // Number of meshes + Mesh *meshes; // Meshes array - int materialCount; - int *meshMaterialId; - Material *materials; + int materialCount; // Number of materials + Material *materials; // Materials array + + int *meshMaterialId; // Mesh materials ids - int jointCount; - Joint *joints; - - 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 273b99314..d417efec4 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 b4cc571d4..99491eeb3 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 3ca696717..4c4f259fa 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 8e26c93f8..197e6eb51 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() @@ -50,6 +51,9 @@ int main() circleC->restitution = 1; SetTargetFPS(60); + + // Restitution demo needs a very tiny physics time step for a proper simulation + SetPhysicsTimeStep(1.0/60.0/100 * 1000); //-------------------------------------------------------------------------------------- // Main game loop @@ -57,6 +61,8 @@ int main() { // Update //---------------------------------------------------------------------------------- + RunPhysicsStep(); + if (IsKeyPressed('R')) // Reset physics input { // Reset circles physics bodies position and velocity @@ -120,4 +126,3 @@ int main() return 0; } - diff --git a/examples/physac/physics_shatter.c b/examples/physac/physics_shatter.c index e34d6cec0..17f9bfc27 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/shapes/shapes_colors_palette.png b/examples/shapes/shapes_colors_palette.png index dd3cf4a57..4073b6701 100644 Binary files a/examples/shapes/shapes_colors_palette.png and b/examples/shapes/shapes_colors_palette.png differ diff --git a/examples/text/resources/custom_alagard.png b/examples/text/resources/custom_alagard.png index c3eb63b71..634265557 100644 Binary files a/examples/text/resources/custom_alagard.png and b/examples/text/resources/custom_alagard.png differ diff --git a/examples/text/resources/custom_jupiter_crash.png b/examples/text/resources/custom_jupiter_crash.png index 451b591f1..2deaaeff7 100644 Binary files a/examples/text/resources/custom_jupiter_crash.png and b/examples/text/resources/custom_jupiter_crash.png differ diff --git a/examples/text/resources/custom_mecha.png b/examples/text/resources/custom_mecha.png index 59caab2cc..a8c9e06e6 100644 Binary files a/examples/text/resources/custom_mecha.png and b/examples/text/resources/custom_mecha.png differ diff --git a/examples/text/resources/fonts/alagard.png b/examples/text/resources/fonts/alagard.png index 3ac4bf1cf..c0c542731 100644 Binary files a/examples/text/resources/fonts/alagard.png and b/examples/text/resources/fonts/alagard.png differ diff --git a/examples/text/resources/fonts/alpha_beta.png b/examples/text/resources/fonts/alpha_beta.png index c362bfb18..8a0c27338 100644 Binary files a/examples/text/resources/fonts/alpha_beta.png and b/examples/text/resources/fonts/alpha_beta.png differ diff --git a/examples/text/resources/fonts/jupiter_crash.png b/examples/text/resources/fonts/jupiter_crash.png index 1f5172fbe..4972c02e9 100644 Binary files a/examples/text/resources/fonts/jupiter_crash.png and b/examples/text/resources/fonts/jupiter_crash.png differ diff --git a/examples/text/resources/fonts/mecha.png b/examples/text/resources/fonts/mecha.png index 8022d18c4..9213fa2d1 100644 Binary files a/examples/text/resources/fonts/mecha.png and b/examples/text/resources/fonts/mecha.png differ diff --git a/examples/text/resources/fonts/pixantiqua.png b/examples/text/resources/fonts/pixantiqua.png index ce422e7e7..17ad1ab7d 100644 Binary files a/examples/text/resources/fonts/pixantiqua.png and b/examples/text/resources/fonts/pixantiqua.png differ diff --git a/examples/text/resources/fonts/pixelplay.png b/examples/text/resources/fonts/pixelplay.png index bf8f8818f..fbf6430d6 100644 Binary files a/examples/text/resources/fonts/pixelplay.png and b/examples/text/resources/fonts/pixelplay.png differ diff --git a/examples/text/resources/fonts/romulus.png b/examples/text/resources/fonts/romulus.png index 46ccc3273..648aa8b06 100644 Binary files a/examples/text/resources/fonts/romulus.png and b/examples/text/resources/fonts/romulus.png differ diff --git a/examples/text/resources/fonts/setback.png b/examples/text/resources/fonts/setback.png index 086f3e27b..1630a7330 100644 Binary files a/examples/text/resources/fonts/setback.png and b/examples/text/resources/fonts/setback.png differ diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index c23a1e2e4..755b642dd 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -29,19 +29,18 @@ int main() fontDefault.baseSize = 16; fontDefault.charsCount = 95; // Parameters > font size: 16, no chars array provided (0), chars count: 95 (autogenerate chars array) - fontDefault.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 95, false); + fontDefault.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 95, FONT_DEFAULT); // Parameters > chars count: 95, font size: 16, chars padding in image: 4 px, pack method: 0 (default) Image atlas = GenImageFontAtlas(fontDefault.chars, 95, 16, 4, 0); fontDefault.texture = LoadTextureFromImage(atlas); UnloadImage(atlas); // SDF font generation from TTF font - // NOTE: SDF chars data is generated with LoadFontData(), it's just a bool option Font fontSDF = { 0 }; fontSDF.baseSize = 16; fontSDF.charsCount = 95; // Parameters > font size: 16, no chars array provided (0), chars count: 0 (defaults to 95) - fontSDF.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 0, true); + fontSDF.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 0, FONT_SDF); // Parameters > chars count: 95, font size: 16, chars padding in image: 0 px, pack method: 1 (Skyline algorythm) atlas = GenImageFontAtlas(fontSDF.chars, 95, 16, 0, 1); fontSDF.texture = LoadTextureFromImage(atlas); diff --git a/examples/textures/resources/custom_jupiter_crash.png b/examples/textures/resources/custom_jupiter_crash.png index 451b591f1..2deaaeff7 100644 Binary files a/examples/textures/resources/custom_jupiter_crash.png and b/examples/textures/resources/custom_jupiter_crash.png differ diff --git a/examples/textures/resources/ninepatch_button.png b/examples/textures/resources/ninepatch_button.png new file mode 100644 index 000000000..d1b599707 Binary files /dev/null and b/examples/textures/resources/ninepatch_button.png differ diff --git a/examples/textures/resources/scarfy.png b/examples/textures/resources/scarfy.png index beb5ffa06..4803ef777 100644 Binary files a/examples/textures/resources/scarfy.png and b/examples/textures/resources/scarfy.png differ diff --git a/examples/textures/textures_image_npatch.c b/examples/textures/textures_image_npatch.c new file mode 100644 index 000000000..357b8a98c --- /dev/null +++ b/examples/textures/textures_image_npatch.c @@ -0,0 +1,108 @@ +/******************************************************************************************* +* +* raylib [textures] example - N-patch drawing +* +* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2016 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - N-patch drawing"); + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + Texture2D nPatchTexture = LoadTexture("resources/ninepatch_button.png"); + Vector2 mousePosition; + Vector2 origin = {0.0f, 0.0f}; + + // The location and size of the n-patches. + Rectangle dstRec1 = {480.0f, 160.0f, 32.0f, 32.0f}; + Rectangle dstRec2 = {160.0f, 160.0f, 32.0f, 32.0f}; + Rectangle dstRecH = {160.0f, 93.0f, 32.0f, 32.0f}; // this rec's height is ignored + Rectangle dstRecV = {92.0f, 160.0f, 32.0f, 32.0f}; // this rec's width is ignored + + // A 9-patch (NPT_9PATCH) changes its sizes in both axis + NPatchInfo ninePatchInfo1 = {(Rectangle){0.0f, 0.0f, 64.0f, 64.0f}, 12, 40, 12, 12, NPT_9PATCH }; + NPatchInfo ninePatchInfo2 = {(Rectangle){0.0f, 128.0f, 64.0f, 64.0f}, 16, 16, 16, 16, NPT_9PATCH }; + // A horizontal 3-patch (NPT_3PATCH_HORIZONTAL) changes its sizes along the x axis only + NPatchInfo h3PatchInfo = {(Rectangle){0.0f, 64.0f, 64.0f, 64.0f}, 8, 8, 8, 8, NPT_3PATCH_HORIZONTAL }; + // A vertical 3-patch (NPT_3PATCH_VERTICAL) changes its sizes along the y axis only + NPatchInfo v3PatchInfo = {(Rectangle){0.0f, 192.0f, 64.0f, 64.0f}, 6, 6, 6, 6, NPT_3PATCH_VERTICAL }; + + SetTargetFPS(60); + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + // resize the n-patches based on mouse position. + dstRec1.width = mousePosition.x - dstRec1.x; + dstRec1.height = mousePosition.y - dstRec1.y; + dstRec2.width = mousePosition.x - dstRec2.x; + dstRec2.height = mousePosition.y - dstRec2.y; + dstRecH.width = mousePosition.x - dstRecH.x; + dstRecV.height = mousePosition.y - dstRecV.y; + + // set a minimum width and/or height + if (dstRec1.width < 1.0f) dstRec1.width = 1.0f; + if (dstRec1.width > 300.0f) dstRec1.width = 300.0f; + if (dstRec1.height < 1.0f) dstRec1.height = 1.0f; + if (dstRec2.width < 1.0f) dstRec2.width = 1.0f; + if (dstRec2.width > 300.0f) dstRec2.width = 300.0f; + if (dstRec2.height < 1.0f) dstRec2.height = 1.0f; + if (dstRecH.width < 1.0f) dstRecH.width = 1.0f; + if (dstRecV.height < 1.0f) dstRecV.height = 1.0f; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw the n-patches + DrawTextureNPatch(nPatchTexture, ninePatchInfo2, dstRec2, origin, 0.0f, WHITE); + DrawTextureNPatch(nPatchTexture, ninePatchInfo1, dstRec1, origin, 0.0f, WHITE); + DrawTextureNPatch(nPatchTexture, h3PatchInfo, dstRecH, origin, 0.0f, WHITE); + DrawTextureNPatch(nPatchTexture, v3PatchInfo, dstRecV, origin, 0.0f, WHITE); + + // Draw the source texture + DrawRectangleLines( 5, 88, 74, 266, BLUE); + DrawTexture(nPatchTexture, 10, 93, WHITE); + DrawText("TEXTURE", 15, 360, 10, DARKGRAY); + + DrawRectangle( 10, 10, 250, 73, Fade(SKYBLUE, 0.5)); + DrawRectangleLines( 10, 10, 250, 73, BLUE); + + DrawText("9-Patch and 3-Patch example", 20, 20, 10, BLACK); + DrawText(" Move the mouse to stretch or", 40, 40, 10, DARKGRAY); + DrawText(" shrink the n-patches.", 40, 60, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(nPatchTexture); // Texture unloading + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/textures/textures_image_npatch.png b/examples/textures/textures_image_npatch.png new file mode 100644 index 000000000..5258811b4 Binary files /dev/null and b/examples/textures/textures_image_npatch.png differ diff --git a/examples/textures/textures_image_processing.c b/examples/textures/textures_image_processing.c index 427faa602..6d33d95bf 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 78d25c14d..fb99e8277 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, RED); 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 842ac77de..3b7dcaa38 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 53ffd1d01..cc08eb58c 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/drturtle/resources/fish.png b/games/drturtle/resources/fish.png index 894dd1cac..dca5956e5 100644 Binary files a/games/drturtle/resources/fish.png and b/games/drturtle/resources/fish.png differ diff --git a/games/drturtle/resources/gamera.png b/games/drturtle/resources/gamera.png index 57127c100..9c2fea71c 100644 Binary files a/games/drturtle/resources/gamera.png and b/games/drturtle/resources/gamera.png differ diff --git a/games/drturtle/resources/gframe.png b/games/drturtle/resources/gframe.png index 87796af23..5946589e6 100644 Binary files a/games/drturtle/resources/gframe.png and b/games/drturtle/resources/gframe.png differ diff --git a/games/drturtle/resources/komika.png b/games/drturtle/resources/komika.png index 174d85b92..2c2ab7142 100644 Binary files a/games/drturtle/resources/komika.png and b/games/drturtle/resources/komika.png differ diff --git a/games/drturtle/resources/mountains.png b/games/drturtle/resources/mountains.png index 169177031..f193f5047 100644 Binary files a/games/drturtle/resources/mountains.png and b/games/drturtle/resources/mountains.png differ diff --git a/games/drturtle/resources/orca.png b/games/drturtle/resources/orca.png index 09aed5741..b580ef072 100644 Binary files a/games/drturtle/resources/orca.png and b/games/drturtle/resources/orca.png differ diff --git a/games/drturtle/resources/sea.png b/games/drturtle/resources/sea.png index ad8f22ef0..34593536d 100644 Binary files a/games/drturtle/resources/sea.png and b/games/drturtle/resources/sea.png differ diff --git a/games/drturtle/resources/shark.png b/games/drturtle/resources/shark.png index 26c05ea5d..24a569574 100644 Binary files a/games/drturtle/resources/shark.png and b/games/drturtle/resources/shark.png differ diff --git a/games/drturtle/resources/sky.png b/games/drturtle/resources/sky.png index 5af8b6f70..e75715c92 100644 Binary files a/games/drturtle/resources/sky.png and b/games/drturtle/resources/sky.png differ diff --git a/games/drturtle/resources/swhale.png b/games/drturtle/resources/swhale.png index be1d0c5c7..5652928c0 100644 Binary files a/games/drturtle/resources/swhale.png and b/games/drturtle/resources/swhale.png differ diff --git a/games/drturtle/resources/title.png b/games/drturtle/resources/title.png index abe0f5b0d..9ddd0de30 100644 Binary files a/games/drturtle/resources/title.png and b/games/drturtle/resources/title.png differ diff --git a/games/drturtle/resources/turtle.png b/games/drturtle/resources/turtle.png index 4abe9f51f..52ce9691f 100644 Binary files a/games/drturtle/resources/turtle.png and b/games/drturtle/resources/turtle.png differ diff --git a/games/just_do/screens/screen_level06.c b/games/just_do/screens/screen_level06.c index a5536aa4a..ec72d70c1 100644 --- a/games/just_do/screens/screen_level06.c +++ b/games/just_do/screens/screen_level06.c @@ -132,7 +132,7 @@ void DrawLevel06Screen(void) DrawRectangleRec(movingRecs[i], GRAY); } - if (!done & (mouseOverNum >= 0)) DrawRectangleLines(movingRecs[mouseOverNum].x - 5, movingRecs[mouseOverNum].y - 5, movingRecs[mouseOverNum].width + 10, movingRecs[mouseOverNum].height + 10, Fade(LIGHTGRAY, 0.8f)); + if (!done && (mouseOverNum >= 0)) DrawRectangleLines(movingRecs[mouseOverNum].x - 5, movingRecs[mouseOverNum].y - 5, movingRecs[mouseOverNum].width + 10, movingRecs[mouseOverNum].height + 10, Fade(LIGHTGRAY, 0.8f)); if (levelFinished) { diff --git a/games/just_do/screens/screen_logo.c b/games/just_do/screens/screen_logo.c index ab0782893..1c9a9b49d 100644 --- a/games/just_do/screens/screen_logo.c +++ b/games/just_do/screens/screen_logo.c @@ -37,7 +37,7 @@ static int framesCounter; static int finishScreen; const char msgLogoA[64] = "A simple and easy-to-use library"; -const char msgLogoB[64] = "to learn videogames programming"; +const char msgLogoB[64] = "to enjoy videogames programming"; int logoPositionX; int logoPositionY; diff --git a/games/koala_seasons/screens/screen_gameplay.c b/games/koala_seasons/screens/screen_gameplay.c index 6bbcfaafc..4d8ff04d6 100644 --- a/games/koala_seasons/screens/screen_gameplay.c +++ b/games/koala_seasons/screens/screen_gameplay.c @@ -954,7 +954,7 @@ void UpdateGameplayScreen(void) playerActive = false; killer = 5; } - else if (CheckCollisionRecs(bee, player) && (state == FINALFORM) && (state != KICK)) + else if (CheckCollisionRecs(bee, player) && (state == FINALFORM)) { isHitBee = true; beeVelocity = 8; diff --git a/games/koala_seasons/screens/screen_logo.c b/games/koala_seasons/screens/screen_logo.c index a3035b309..55785c3a5 100644 --- a/games/koala_seasons/screens/screen_logo.c +++ b/games/koala_seasons/screens/screen_logo.c @@ -37,7 +37,7 @@ static int framesCounter; static int finishScreen; const char msgLogoA[64] = "A simple and easy-to-use library"; -const char msgLogoB[64] = "to learn videogames programming"; +const char msgLogoB[64] = "to enjoy videogames programming"; int logoPositionX; int logoPositionY; diff --git a/games/koala_seasons/screens/screen_title.c b/games/koala_seasons/screens/screen_title.c index 0459906dc..21a4df51d 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/games/light_my_ritual/resources/font_arcadian.png b/games/light_my_ritual/resources/font_arcadian.png index 5c3df51ac..7abcc197f 100644 Binary files a/games/light_my_ritual/resources/font_arcadian.png and b/games/light_my_ritual/resources/font_arcadian.png differ diff --git a/games/light_my_ritual/resources/textures/back_title.png b/games/light_my_ritual/resources/textures/back_title.png index 549212478..c6cf96984 100644 Binary files a/games/light_my_ritual/resources/textures/back_title.png and b/games/light_my_ritual/resources/textures/back_title.png differ diff --git a/games/light_my_ritual/resources/textures/background.png b/games/light_my_ritual/resources/textures/background.png index 367bdd30c..78dd3e212 100644 Binary files a/games/light_my_ritual/resources/textures/background.png and b/games/light_my_ritual/resources/textures/background.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_i_off.png b/games/light_my_ritual/resources/textures/circle_level_i_off.png index 7961af7d4..4fe966189 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_i_off.png and b/games/light_my_ritual/resources/textures/circle_level_i_off.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_i_on.png b/games/light_my_ritual/resources/textures/circle_level_i_on.png index 1f2177349..3ac21705c 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_i_on.png and b/games/light_my_ritual/resources/textures/circle_level_i_on.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_ii_off.png b/games/light_my_ritual/resources/textures/circle_level_ii_off.png index 642adc3be..d97b7df17 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_ii_off.png and b/games/light_my_ritual/resources/textures/circle_level_ii_off.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_ii_on.png b/games/light_my_ritual/resources/textures/circle_level_ii_on.png index 93b71b3a5..c4a1d710f 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_ii_on.png and b/games/light_my_ritual/resources/textures/circle_level_ii_on.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_iii_off.png b/games/light_my_ritual/resources/textures/circle_level_iii_off.png index daf50be95..fe92c4f90 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_iii_off.png and b/games/light_my_ritual/resources/textures/circle_level_iii_off.png differ diff --git a/games/light_my_ritual/resources/textures/circle_level_iii_on.png b/games/light_my_ritual/resources/textures/circle_level_iii_on.png index e2256e95c..2c986595e 100644 Binary files a/games/light_my_ritual/resources/textures/circle_level_iii_on.png and b/games/light_my_ritual/resources/textures/circle_level_iii_on.png differ diff --git a/games/light_my_ritual/resources/textures/enemy.png b/games/light_my_ritual/resources/textures/enemy.png index dc0a911d2..7f51ab59a 100644 Binary files a/games/light_my_ritual/resources/textures/enemy.png and b/games/light_my_ritual/resources/textures/enemy.png differ diff --git a/games/light_my_ritual/resources/textures/foreground_level_i.png b/games/light_my_ritual/resources/textures/foreground_level_i.png index 32b96740d..58a5797a9 100644 Binary files a/games/light_my_ritual/resources/textures/foreground_level_i.png and b/games/light_my_ritual/resources/textures/foreground_level_i.png differ diff --git a/games/light_my_ritual/resources/textures/foreground_level_ii.png b/games/light_my_ritual/resources/textures/foreground_level_ii.png index 44f26003a..b9e30648c 100644 Binary files a/games/light_my_ritual/resources/textures/foreground_level_ii.png and b/games/light_my_ritual/resources/textures/foreground_level_ii.png differ diff --git a/games/light_my_ritual/resources/textures/foreground_level_iii.png b/games/light_my_ritual/resources/textures/foreground_level_iii.png index 0fc039d72..054aa8dcf 100644 Binary files a/games/light_my_ritual/resources/textures/foreground_level_iii.png and b/games/light_my_ritual/resources/textures/foreground_level_iii.png differ diff --git a/games/light_my_ritual/resources/textures/light.png b/games/light_my_ritual/resources/textures/light.png index 5d08326f8..98829c76a 100644 Binary files a/games/light_my_ritual/resources/textures/light.png and b/games/light_my_ritual/resources/textures/light.png differ diff --git a/games/light_my_ritual/resources/textures/light_glow.png b/games/light_my_ritual/resources/textures/light_glow.png index d31356c6d..a0bc845b7 100644 Binary files a/games/light_my_ritual/resources/textures/light_glow.png and b/games/light_my_ritual/resources/textures/light_glow.png differ diff --git a/games/light_my_ritual/resources/textures/light_ray.png b/games/light_my_ritual/resources/textures/light_ray.png index f9f877fa4..68c9e766b 100644 Binary files a/games/light_my_ritual/resources/textures/light_ray.png and b/games/light_my_ritual/resources/textures/light_ray.png differ diff --git a/games/light_my_ritual/resources/textures/msg_ritual.png b/games/light_my_ritual/resources/textures/msg_ritual.png index 73f14d484..fa256cc70 100644 Binary files a/games/light_my_ritual/resources/textures/msg_ritual.png and b/games/light_my_ritual/resources/textures/msg_ritual.png differ diff --git a/games/light_my_ritual/resources/textures/player.png b/games/light_my_ritual/resources/textures/player.png index d6849478c..637f1a253 100644 Binary files a/games/light_my_ritual/resources/textures/player.png and b/games/light_my_ritual/resources/textures/player.png differ diff --git a/games/light_my_ritual/resources/textures/time_over.png b/games/light_my_ritual/resources/textures/time_over.png index 9f7935bea..971334ab2 100644 Binary files a/games/light_my_ritual/resources/textures/time_over.png and b/games/light_my_ritual/resources/textures/time_over.png differ diff --git a/games/light_my_ritual/resources/textures/title.png b/games/light_my_ritual/resources/textures/title.png index 55eb593dd..a1a0c86eb 100644 Binary files a/games/light_my_ritual/resources/textures/title.png and b/games/light_my_ritual/resources/textures/title.png differ diff --git a/games/skully_escape/resources/textures/background_aisle01.png b/games/skully_escape/resources/textures/background_aisle01.png index 482056f8d..32fe5b699 100644 Binary files a/games/skully_escape/resources/textures/background_aisle01.png and b/games/skully_escape/resources/textures/background_aisle01.png differ diff --git a/games/skully_escape/resources/textures/doors.png b/games/skully_escape/resources/textures/doors.png index f5a75d058..ff33169d9 100644 Binary files a/games/skully_escape/resources/textures/doors.png and b/games/skully_escape/resources/textures/doors.png differ diff --git a/games/skully_escape/resources/textures/monster_chair_left.png b/games/skully_escape/resources/textures/monster_chair_left.png index b3ac070f3..a47740324 100644 Binary files a/games/skully_escape/resources/textures/monster_chair_left.png and b/games/skully_escape/resources/textures/monster_chair_left.png differ diff --git a/games/skully_escape/resources/textures/monster_chair_right.png b/games/skully_escape/resources/textures/monster_chair_right.png index d29cc0211..8d3007a3c 100644 Binary files a/games/skully_escape/resources/textures/monster_chair_right.png and b/games/skully_escape/resources/textures/monster_chair_right.png differ diff --git a/games/skully_escape/resources/textures/monster_lamp_left.png b/games/skully_escape/resources/textures/monster_lamp_left.png index 3b31b77f5..d95dd2486 100644 Binary files a/games/skully_escape/resources/textures/monster_lamp_left.png and b/games/skully_escape/resources/textures/monster_lamp_left.png differ diff --git a/games/skully_escape/resources/textures/monster_lamp_right.png b/games/skully_escape/resources/textures/monster_lamp_right.png index 26491866b..81bbc6d6a 100644 Binary files a/games/skully_escape/resources/textures/monster_lamp_right.png and b/games/skully_escape/resources/textures/monster_lamp_right.png differ diff --git a/games/skully_escape/resources/textures/monster_picture.png b/games/skully_escape/resources/textures/monster_picture.png index 29549895b..f4f9d84be 100644 Binary files a/games/skully_escape/resources/textures/monster_picture.png and b/games/skully_escape/resources/textures/monster_picture.png differ diff --git a/games/skully_escape/resources/textures/monster_window.png b/games/skully_escape/resources/textures/monster_window.png index 47d70a793..4a20a5c64 100644 Binary files a/games/skully_escape/resources/textures/monster_window.png and b/games/skully_escape/resources/textures/monster_window.png differ diff --git a/games/skully_escape/resources/textures/skully.png b/games/skully_escape/resources/textures/skully.png index 9ce6cd583..5fec59397 100644 Binary files a/games/skully_escape/resources/textures/skully.png and b/games/skully_escape/resources/textures/skully.png differ diff --git a/games/snake.c b/games/snake.c index 40d66da86..23d1bd1d0 100644 --- a/games/snake.c +++ b/games/snake.c @@ -203,7 +203,7 @@ void UpdateGame(void) if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) gameOver = true; } - // TODO: review logic: fruit.position calculation + // fruit.position calculation if (!fruit.active) { fruit.active = true; @@ -211,17 +211,17 @@ void UpdateGame(void) for (int i = 0; i < counterTail; i++) { - while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y)) - { - fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE }; - i = 0; - } + while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y)) + { + fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 }; + i = 0; + } } } // collision - if (CheckCollisionRecs((Rectangle){(int)snake[0].position.x, (int)snake[0].position.y, (int)snake[0].size.x, (int)snake[0].size.y}, - (Rectangle){(int)fruit.position.x, (int)fruit.position.y, (int)fruit.size.x, (int)fruit.size.y})) + if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) && + (snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y)) { snake[counterTail].position = snakePosition[counterTail - 1]; counterTail += 1; diff --git a/games/transmission/resources/textures/ending_background.png b/games/transmission/resources/textures/ending_background.png index 6577be63b..208886588 100644 Binary files a/games/transmission/resources/textures/ending_background.png and b/games/transmission/resources/textures/ending_background.png differ diff --git a/games/transmission/resources/textures/message_background.png b/games/transmission/resources/textures/message_background.png index 78c00a597..fb544796e 100644 Binary files a/games/transmission/resources/textures/message_background.png and b/games/transmission/resources/textures/message_background.png differ diff --git a/games/transmission/resources/textures/message_vignette.png b/games/transmission/resources/textures/message_vignette.png index 72234c594..6ef64b6bc 100644 Binary files a/games/transmission/resources/textures/message_vignette.png and b/games/transmission/resources/textures/message_vignette.png differ diff --git a/games/transmission/resources/textures/mission_background.png b/games/transmission/resources/textures/mission_background.png index 4efe58155..da5f3f67a 100644 Binary files a/games/transmission/resources/textures/mission_background.png and b/games/transmission/resources/textures/mission_background.png differ diff --git a/games/transmission/resources/textures/mission_backline.png b/games/transmission/resources/textures/mission_backline.png index 2a89d80a6..6123a9845 100644 Binary files a/games/transmission/resources/textures/mission_backline.png and b/games/transmission/resources/textures/mission_backline.png differ diff --git a/games/transmission/resources/textures/title_background.png b/games/transmission/resources/textures/title_background.png index c90d880d5..e45bbec80 100644 Binary files a/games/transmission/resources/textures/title_background.png and b/games/transmission/resources/textures/title_background.png differ diff --git a/games/transmission/resources/textures/title_ribbon.png b/games/transmission/resources/textures/title_ribbon.png index 1526eba17..f75438dd6 100644 Binary files a/games/transmission/resources/textures/title_ribbon.png and b/games/transmission/resources/textures/title_ribbon.png differ diff --git a/games/transmission/resources/textures/words_base.png b/games/transmission/resources/textures/words_base.png index a21390301..e79e9815e 100644 Binary files a/games/transmission/resources/textures/words_base.png and b/games/transmission/resources/textures/words_base.png differ diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index 0aba5f014..d0cf7d899 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -110,10 +110,12 @@ void InitEndingScreen(void) { // WARNING: It fails if the last sentence word has a '.' after space char *title = StringReplace(headline, messageWords[i].text, codingWords[messageWords[i].id]); - - strcpy(headline, title); // Base headline updated - - if (title != NULL) free(title); + + if (title != NULL) + { + strcpy(headline, title); // Base headline updated + free(title); + } } } diff --git a/games/wave_collector/resources/textures/background.png b/games/wave_collector/resources/textures/background.png index f8613f829..b4f767f75 100644 Binary files a/games/wave_collector/resources/textures/background.png and b/games/wave_collector/resources/textures/background.png differ diff --git a/games/wave_collector/resources/textures/background_gameplay.png b/games/wave_collector/resources/textures/background_gameplay.png index c6a510705..9be7d17de 100644 Binary files a/games/wave_collector/resources/textures/background_gameplay.png and b/games/wave_collector/resources/textures/background_gameplay.png differ diff --git a/games/wave_collector/resources/textures/background_title.png b/games/wave_collector/resources/textures/background_title.png index 01fc46d3e..19546d863 100644 Binary files a/games/wave_collector/resources/textures/background_title.png and b/games/wave_collector/resources/textures/background_title.png differ diff --git a/games/wave_collector/resources/textures/icon_synchro.png b/games/wave_collector/resources/textures/icon_synchro.png index b71728237..841f826b7 100644 Binary files a/games/wave_collector/resources/textures/icon_synchro.png and b/games/wave_collector/resources/textures/icon_synchro.png differ diff --git a/games/wave_collector/resources/textures/icon_warp.png b/games/wave_collector/resources/textures/icon_warp.png index 2a5eb7bb7..e91f43d4a 100644 Binary files a/games/wave_collector/resources/textures/icon_warp.png and b/games/wave_collector/resources/textures/icon_warp.png differ diff --git a/games/wave_collector/resources/textures/lose.png b/games/wave_collector/resources/textures/lose.png index f01c02848..ba69a4c28 100644 Binary files a/games/wave_collector/resources/textures/lose.png and b/games/wave_collector/resources/textures/lose.png differ diff --git a/games/wave_collector/resources/textures/player.png b/games/wave_collector/resources/textures/player.png index c8913a8f1..86b843b29 100644 Binary files a/games/wave_collector/resources/textures/player.png and b/games/wave_collector/resources/textures/player.png differ diff --git a/games/wave_collector/resources/textures/sample_big.png b/games/wave_collector/resources/textures/sample_big.png index b4c4be975..6fb20b638 100644 Binary files a/games/wave_collector/resources/textures/sample_big.png and b/games/wave_collector/resources/textures/sample_big.png differ diff --git a/games/wave_collector/resources/textures/sample_small.png b/games/wave_collector/resources/textures/sample_small.png index 7b624f7f0..5fa7d01ba 100644 Binary files a/games/wave_collector/resources/textures/sample_small.png and b/games/wave_collector/resources/textures/sample_small.png differ diff --git a/games/wave_collector/resources/textures/win.png b/games/wave_collector/resources/textures/win.png index d7afdc25d..7d539f66b 100644 Binary files a/games/wave_collector/resources/textures/win.png and b/games/wave_collector/resources/textures/win.png differ diff --git a/logo/raylib.ico b/logo/raylib.ico index afeb12b91..0cedcc55c 100644 Binary files a/logo/raylib.ico and b/logo/raylib.ico differ diff --git a/logo/raylib_180x180.png b/logo/raylib_180x180.png new file mode 100644 index 000000000..1828fb332 Binary files /dev/null and b/logo/raylib_180x180.png differ diff --git a/logo/raylib_36x36.png b/logo/raylib_36x36.png index cc72255f4..1346789bf 100644 Binary files a/logo/raylib_36x36.png and b/logo/raylib_36x36.png differ diff --git a/logo/raylib_512x512.png b/logo/raylib_512x512.png index 0b8c48ec5..599549115 100644 Binary files a/logo/raylib_512x512.png and b/logo/raylib_512x512.png differ diff --git a/logo/raylib_72x72.png b/logo/raylib_72x72.png index 34a6155e6..b1c1f553d 100644 Binary files a/logo/raylib_72x72.png and b/logo/raylib_72x72.png differ diff --git a/logo/raylib_96x96.png b/logo/raylib_96x96.png index f2774d221..d211d8f46 100644 Binary files a/logo/raylib_96x96.png and b/logo/raylib_96x96.png differ diff --git a/projects/Notepad++/npes_saved.txt b/projects/Notepad++/npes_saved.txt deleted file mode 100644 index fd62b75d4..000000000 Binary files a/projects/Notepad++/npes_saved.txt and /dev/null differ diff --git a/projects/Notepad++/npes_saved_mingw.txt b/projects/Notepad++/npes_saved_mingw.txt index 27b102035..0c349c25d 100644 Binary files a/projects/Notepad++/npes_saved_mingw.txt and b/projects/Notepad++/npes_saved_mingw.txt differ diff --git a/projects/Notepad++/npes_saved_tcc.txt b/projects/Notepad++/npes_saved_tcc.txt index c997f90fb..7be483efd 100644 Binary files a/projects/Notepad++/npes_saved_tcc.txt and b/projects/Notepad++/npes_saved_tcc.txt differ diff --git a/projects/README.md b/projects/README.md index 3b152f983..6cb5798c7 100644 --- a/projects/README.md +++ b/projects/README.md @@ -12,6 +12,6 @@ IDE | Platform | Template type | State [Notepad++](https://notepad-plus-plus.org/) | Windows | source/example compiling | DONE [VS2015](https://www.visualstudio.com) | Windows | source/example compiling | DONE [VS2017](https://www.visualstudio.com) | Windows | source/example compiling | DONE -[VSCode](https://code.visualstudio.com/) | Windows | - | INCOMPLETE +[VSCode](https://code.visualstudio.com/) | Windows, macOS | - | INCOMPLETE *New IDEs config files are welcome!* diff --git a/projects/SublimeText/README.md b/projects/SublimeText/README.md new file mode 100644 index 000000000..5100d6435 --- /dev/null +++ b/projects/SublimeText/README.md @@ -0,0 +1,13 @@ +# Sublime Text 3 project template + +This is a project template to be used with [Sublime Text 3](https://www.sublimetext.com/). + +Simply click on the `raylib.sublime-project` file to open it with Sublime Text. +Alternatively you can open Sublime Text first and click on the `Project -> Open Project`. + +It comes with raylib.sublime-build. This file needs to be copied into the sublime packages folder for the user. On windows it can be found in `%AppData%\Sublime Text 3\Packages\User`. + +Once done open the project and select `Tools -> Build System -> raylib`. +To run press Ctrl + Shift + B and select which build you want to run. + +For a full overview of build systems please check the [build systems guide](https://www.sublimetext.com/docs/3/build_systems.html). diff --git a/projects/SublimeText/raylib.sublime-build b/projects/SublimeText/raylib.sublime-build new file mode 100644 index 000000000..faa02dd87 --- /dev/null +++ b/projects/SublimeText/raylib.sublime-build @@ -0,0 +1,13 @@ +{ + "variants": [ + { + "name": "desktop", + "shell_cmd": "cd ${folder}\\src && mingw32-make PLATFORM=PLATFORM_DESKTOP" + }, + + { + "name": "examples", + "shell_cmd": "cd ${folder}\\examples && mingw32-make PLATFORM=PLATFORM_DESKTOP" + }, + ] +} diff --git a/projects/SublimeText/raylib.sublime-project b/projects/SublimeText/raylib.sublime-project new file mode 100644 index 000000000..5eaa1a794 --- /dev/null +++ b/projects/SublimeText/raylib.sublime-project @@ -0,0 +1,8 @@ +{ + "folders": + [ + { + "path": "../../" + } + ] +} diff --git a/projects/VS2017/examples/core_basic_window.vcxproj b/projects/VS2017/examples/core_basic_window.vcxproj index e7f28fb96..48e06e448 100644 --- a/projects/VS2017/examples/core_basic_window.vcxproj +++ b/projects/VS2017/examples/core_basic_window.vcxproj @@ -98,7 +98,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console @@ -115,7 +115,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console @@ -133,7 +133,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -155,7 +155,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index 0467ee225..39a2aee77 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -97,7 +97,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsCpp @@ -114,7 +114,7 @@ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsCpp @@ -134,7 +134,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) CompileAsCpp - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console @@ -155,7 +155,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) CompileAsCpp - $(SolutionDir)..\..\release\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) Console diff --git a/projects/VSCode/.vscode/c_cpp_properties.json b/projects/VSCode/.vscode/c_cpp_properties.json index 10494f677..496a9b2df 100644 --- a/projects/VSCode/.vscode/c_cpp_properties.json +++ b/projects/VSCode/.vscode/c_cpp_properties.json @@ -17,7 +17,47 @@ "cStandard": "c11", "cppStandard": "c++14", "intelliSenseMode": "clang-x64" + }, + { + "name": "Mac", + "includePath": [ + "/src/**", + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "GRAPHICS_API_OPENGL_33", + "PLATFORM_DESKTOP" + ], + "macFrameworkPath": [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++14", + "intelliSenseMode": "clang-x64" + }, + { + "name": "Linux", + "includePath": [ + "/src/**", + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "GRAPHICS_API_OPENGL_33", + "PLATFORM_DESKTOP" + ], + "compilerPath": "usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++14", + "intelliSenseMode": "clang-x64" + } ], "version": 4 -} \ No newline at end of file +} diff --git a/projects/VSCode/.vscode/launch.json b/projects/VSCode/.vscode/launch.json index 4575074f9..8c8a67ead 100644 --- a/projects/VSCode/.vscode/launch.json +++ b/projects/VSCode/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Debug", + "name": "(WIN) Debug", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/game.exe", @@ -23,10 +23,10 @@ "ignoreFailures": false } ], - "preLaunchTask": "build debug" + "preLaunchTask": "(WIN) build debug" }, { - "name": "Run", + "name": "(WIN) Run", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/game.exe", @@ -44,7 +44,67 @@ "ignoreFailures": false } ], - "preLaunchTask": "build release" + "preLaunchTask": "(WIN) build release" }, + { + "name": "(OSX) Debug", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/game", + "args": [], + "cwd": "${workspaceFolder}", + "preLaunchTask": "(OSX) build debug" + }, + { + "name": "(OSX) Run", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/game", + "args": [], + "cwd": "${workspaceFolder}", + "preLaunchTask": "(OSX) build release" + }, + { + "name": "(GNU) Debug", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/game", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": false + } + ], + "preLaunchTask": "(GNU) build debug" + }, + { + "name": "(GNU) Run", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/game", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": false + } + ], + "preLaunchTask": "(GNU) build release" + } ] - } \ No newline at end of file + } diff --git a/projects/VSCode/.vscode/tasks.json b/projects/VSCode/.vscode/tasks.json index cef9e9604..d94a2d692 100644 --- a/projects/VSCode/.vscode/tasks.json +++ b/projects/VSCode/.vscode/tasks.json @@ -4,30 +4,68 @@ "version": "2.0.0", "tasks": [ { - "label": "build release", + "label": "(WIN) build release", "type": "process", "command": "C:/raylib/mingw/bin/mingw32-make.exe", "args": [ "PLATFORM=PLATFORM_DESKTOP", "RAYLIB_PATH=C:/raylib/raylib", - "PROJECT_NAME=game", ], "group": "build" }, { - "label": "build debug", + "label": "(WIN) build debug", "type": "process", "command": "C:/raylib/mingw/bin/mingw32-make.exe", "args": [ "PLATFORM=PLATFORM_DESKTOP", "RAYLIB_PATH=C:/raylib/raylib", - "PROJECT_NAME=game", - "DEBUGGING=TRUE", + "DEBUGGING=TRUE" + ], + "group": "build" + }, + { + "label": "(OSX) build debug", + "type": "process", + "command": "make", + "args": [ + "PLATFORM=PLATFORM_DESKTOP", + "RAYLIB_PATH=", + "DEBUGGING=TRUE" + ], + "group": "build" + }, + { + "label": "(OSX) build release", + "type": "process", + "command": "make", + "args": [ + "PLATFORM=PLATFORM_DESKTOP", + "RAYLIB_PATH=", + ], + "group": "build" + }, + { + "label": "(GNU) build debug", + "type": "process", + "command": "make", + "args": [ + "PLATFORM=PLATFORM_DESKTOP", + "DEBUGGING=TRUE" ], "group": { "kind": "build", "isDefault": true } + }, + { + "label": "(GNU) build release", + "type": "process", + "command": "make", + "args": [ + "PLATFORM=PLATFORM_DESKTOP", + ], + "group": "build" } ] -} \ No newline at end of file +} diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 2f78971b2..9794c1ab2 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -27,7 +27,7 @@ # WARNING: To compile to HTML5, code must be redesigned to use emscripten.h and emscripten_set_main_loop() PLATFORM ?= PLATFORM_DESKTOP RAYLIB_PATH ?= ../.. -PROJECT_NAME ?= standard_game +PROJECT_NAME ?= game DEBUGGING ?= FALSE # Default path for raylib on Raspberry Pi, if installed in different path, update it! @@ -254,7 +254,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),OSX) # Libraries for OSX 10.9 desktop compiling # NOTE: Required packages: libopenal-dev libegl1-mesa-dev - LDLIBS = -lraylib -framework OpenGL -framework OpenAL -framework Cocoa + LDLIBS = -lraylib -framework OpenGL -framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo endif ifeq ($(PLATFORM_OS),BSD) # Libraries for FreeBSD, OpenBSD, NetBSD, DragonFly desktop compiling diff --git a/raylib.pc.in b/raylib.pc.in index c6d543892..837b2d9a1 100644 --- a/raylib.pc.in +++ b/raylib.pc.in @@ -4,7 +4,7 @@ libdir=${exec_prefix}/lib includedir=${prefix}/include Name: raylib -Description: Simple and easy-to-use library to learn videogames programming +Description: Simple and easy-to-use library to enjoy videogames programming URL: http://github.com/raysan5/raylib Version: @PROJECT_VERSION@ Libs: -L${libdir} -lraylib @PKG_CONFIG_LIBS_EXTRA@ diff --git a/raylib.rc.o b/raylib.rc.o index 28005c734..fa27177d2 100644 Binary files a/raylib.rc.o and b/raylib.rc.o differ diff --git a/release/README.md b/release/README.md deleted file mode 100644 index 3c5c7a6e9..000000000 --- a/release/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## raylib release - -In this folder you can find raylib library pre-compiled and ready-to-use for different platforms. - -For detailed information on raylib external dependencies, just check [Wiki information on external dependencies](https://github.com/raysan5/raylib/wiki/raylib-external-dependencies). - -**NOTE: Next raylib version is removing all external dependencies.** diff --git a/release/include/AL/al.h b/release/include/AL/al.h deleted file mode 100644 index 413b38331..000000000 --- a/release/include/AL/al.h +++ /dev/null @@ -1,656 +0,0 @@ -#ifndef AL_AL_H -#define AL_AL_H - -#if defined(__cplusplus) -extern "C" { -#endif - -#ifndef AL_API - #if defined(AL_LIBTYPE_STATIC) - #define AL_API - #elif defined(_WIN32) - #define AL_API __declspec(dllimport) - #else - #define AL_API extern - #endif -#endif - -#if defined(_WIN32) - #define AL_APIENTRY __cdecl -#else - #define AL_APIENTRY -#endif - - -/** Deprecated macro. */ -#define OPENAL -#define ALAPI AL_API -#define ALAPIENTRY AL_APIENTRY -#define AL_INVALID (-1) -#define AL_ILLEGAL_ENUM AL_INVALID_ENUM -#define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION - -/** Supported AL version. */ -#define AL_VERSION_1_0 -#define AL_VERSION_1_1 - -/** 8-bit boolean */ -typedef char ALboolean; - -/** character */ -typedef char ALchar; - -/** signed 8-bit 2's complement integer */ -typedef signed char ALbyte; - -/** unsigned 8-bit integer */ -typedef unsigned char ALubyte; - -/** signed 16-bit 2's complement integer */ -typedef short ALshort; - -/** unsigned 16-bit integer */ -typedef unsigned short ALushort; - -/** signed 32-bit 2's complement integer */ -typedef int ALint; - -/** unsigned 32-bit integer */ -typedef unsigned int ALuint; - -/** non-negative 32-bit binary integer size */ -typedef int ALsizei; - -/** enumerated 32-bit value */ -typedef int ALenum; - -/** 32-bit IEEE754 floating-point */ -typedef float ALfloat; - -/** 64-bit IEEE754 floating-point */ -typedef double ALdouble; - -/** void type (for opaque pointers only) */ -typedef void ALvoid; - - -/* Enumerant values begin at column 50. No tabs. */ - -/** "no distance model" or "no buffer" */ -#define AL_NONE 0 - -/** Boolean False. */ -#define AL_FALSE 0 - -/** Boolean True. */ -#define AL_TRUE 1 - - -/** - * Relative source. - * Type: ALboolean - * Range: [AL_TRUE, AL_FALSE] - * Default: AL_FALSE - * - * Specifies if the Source has relative coordinates. - */ -#define AL_SOURCE_RELATIVE 0x202 - - -/** - * Inner cone angle, in degrees. - * Type: ALint, ALfloat - * Range: [0 - 360] - * Default: 360 - * - * The angle covered by the inner cone, where the source will not attenuate. - */ -#define AL_CONE_INNER_ANGLE 0x1001 - -/** - * Outer cone angle, in degrees. - * Range: [0 - 360] - * Default: 360 - * - * The angle covered by the outer cone, where the source will be fully - * attenuated. - */ -#define AL_CONE_OUTER_ANGLE 0x1002 - -/** - * Source pitch. - * Type: ALfloat - * Range: [0.5 - 2.0] - * Default: 1.0 - * - * A multiplier for the frequency (sample rate) of the source's buffer. - */ -#define AL_PITCH 0x1003 - -/** - * Source or listener position. - * Type: ALfloat[3], ALint[3] - * Default: {0, 0, 0} - * - * The source or listener location in three dimensional space. - * - * OpenAL, like OpenGL, uses a right handed coordinate system, where in a - * frontal default view X (thumb) points right, Y points up (index finger), and - * Z points towards the viewer/camera (middle finger). - * - * To switch from a left handed coordinate system, flip the sign on the Z - * coordinate. - */ -#define AL_POSITION 0x1004 - -/** - * Source direction. - * Type: ALfloat[3], ALint[3] - * Default: {0, 0, 0} - * - * Specifies the current direction in local space. - * A zero-length vector specifies an omni-directional source (cone is ignored). - */ -#define AL_DIRECTION 0x1005 - -/** - * Source or listener velocity. - * Type: ALfloat[3], ALint[3] - * Default: {0, 0, 0} - * - * Specifies the current velocity in local space. - */ -#define AL_VELOCITY 0x1006 - -/** - * Source looping. - * Type: ALboolean - * Range: [AL_TRUE, AL_FALSE] - * Default: AL_FALSE - * - * Specifies whether source is looping. - */ -#define AL_LOOPING 0x1007 - -/** - * Source buffer. - * Type: ALuint - * Range: any valid Buffer. - * - * Specifies the buffer to provide sound samples. - */ -#define AL_BUFFER 0x1009 - -/** - * Source or listener gain. - * Type: ALfloat - * Range: [0.0 - ] - * - * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation - * of about -6dB. Each multiplicaton by 2 equals an amplification of about - * +6dB. - * - * A value of 0.0 is meaningless with respect to a logarithmic scale; it is - * silent. - */ -#define AL_GAIN 0x100A - -/** - * Minimum source gain. - * Type: ALfloat - * Range: [0.0 - 1.0] - * - * The minimum gain allowed for a source, after distance and cone attenation is - * applied (if applicable). - */ -#define AL_MIN_GAIN 0x100D - -/** - * Maximum source gain. - * Type: ALfloat - * Range: [0.0 - 1.0] - * - * The maximum gain allowed for a source, after distance and cone attenation is - * applied (if applicable). - */ -#define AL_MAX_GAIN 0x100E - -/** - * Listener orientation. - * Type: ALfloat[6] - * Default: {0.0, 0.0, -1.0, 0.0, 1.0, 0.0} - * - * Effectively two three dimensional vectors. The first vector is the front (or - * "at") and the second is the top (or "up"). - * - * Both vectors are in local space. - */ -#define AL_ORIENTATION 0x100F - -/** - * Source state (query only). - * Type: ALint - * Range: [AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED] - */ -#define AL_SOURCE_STATE 0x1010 - -/** Source state value. */ -#define AL_INITIAL 0x1011 -#define AL_PLAYING 0x1012 -#define AL_PAUSED 0x1013 -#define AL_STOPPED 0x1014 - -/** - * Source Buffer Queue size (query only). - * Type: ALint - * - * The number of buffers queued using alSourceQueueBuffers, minus the buffers - * removed with alSourceUnqueueBuffers. - */ -#define AL_BUFFERS_QUEUED 0x1015 - -/** - * Source Buffer Queue processed count (query only). - * Type: ALint - * - * The number of queued buffers that have been fully processed, and can be - * removed with alSourceUnqueueBuffers. - * - * Looping sources will never fully process buffers because they will be set to - * play again for when the source loops. - */ -#define AL_BUFFERS_PROCESSED 0x1016 - -/** - * Source reference distance. - * Type: ALfloat - * Range: [0.0 - ] - * Default: 1.0 - * - * The distance in units that no attenuation occurs. - * - * At 0.0, no distance attenuation ever occurs on non-linear attenuation models. - */ -#define AL_REFERENCE_DISTANCE 0x1020 - -/** - * Source rolloff factor. - * Type: ALfloat - * Range: [0.0 - ] - * Default: 1.0 - * - * Multiplier to exaggerate or diminish distance attenuation. - * - * At 0.0, no distance attenuation ever occurs. - */ -#define AL_ROLLOFF_FACTOR 0x1021 - -/** - * Outer cone gain. - * Type: ALfloat - * Range: [0.0 - 1.0] - * Default: 0.0 - * - * The gain attenuation applied when the listener is outside of the source's - * outer cone. - */ -#define AL_CONE_OUTER_GAIN 0x1022 - -/** - * Source maximum distance. - * Type: ALfloat - * Range: [0.0 - ] - * Default: +inf - * - * The distance above which the source is not attenuated any further with a - * clamped distance model, or where attenuation reaches 0.0 gain for linear - * distance models with a default rolloff factor. - */ -#define AL_MAX_DISTANCE 0x1023 - -/** Source buffer position, in seconds */ -#define AL_SEC_OFFSET 0x1024 -/** Source buffer position, in sample frames */ -#define AL_SAMPLE_OFFSET 0x1025 -/** Source buffer position, in bytes */ -#define AL_BYTE_OFFSET 0x1026 - -/** - * Source type (query only). - * Type: ALint - * Range: [AL_STATIC, AL_STREAMING, AL_UNDETERMINED] - * - * A Source is Static if a Buffer has been attached using AL_BUFFER. - * - * A Source is Streaming if one or more Buffers have been attached using - * alSourceQueueBuffers. - * - * A Source is Undetermined when it has the NULL buffer attached using - * AL_BUFFER. - */ -#define AL_SOURCE_TYPE 0x1027 - -/** Source type value. */ -#define AL_STATIC 0x1028 -#define AL_STREAMING 0x1029 -#define AL_UNDETERMINED 0x1030 - -/** Buffer format specifier. */ -#define AL_FORMAT_MONO8 0x1100 -#define AL_FORMAT_MONO16 0x1101 -#define AL_FORMAT_STEREO8 0x1102 -#define AL_FORMAT_STEREO16 0x1103 - -/** Buffer frequency (query only). */ -#define AL_FREQUENCY 0x2001 -/** Buffer bits per sample (query only). */ -#define AL_BITS 0x2002 -/** Buffer channel count (query only). */ -#define AL_CHANNELS 0x2003 -/** Buffer data size (query only). */ -#define AL_SIZE 0x2004 - -/** - * Buffer state. - * - * Not for public use. - */ -#define AL_UNUSED 0x2010 -#define AL_PENDING 0x2011 -#define AL_PROCESSED 0x2012 - - -/** No error. */ -#define AL_NO_ERROR 0 - -/** Invalid name paramater passed to AL call. */ -#define AL_INVALID_NAME 0xA001 - -/** Invalid enum parameter passed to AL call. */ -#define AL_INVALID_ENUM 0xA002 - -/** Invalid value parameter passed to AL call. */ -#define AL_INVALID_VALUE 0xA003 - -/** Illegal AL call. */ -#define AL_INVALID_OPERATION 0xA004 - -/** Not enough memory. */ -#define AL_OUT_OF_MEMORY 0xA005 - - -/** Context string: Vendor ID. */ -#define AL_VENDOR 0xB001 -/** Context string: Version. */ -#define AL_VERSION 0xB002 -/** Context string: Renderer ID. */ -#define AL_RENDERER 0xB003 -/** Context string: Space-separated extension list. */ -#define AL_EXTENSIONS 0xB004 - - -/** - * Doppler scale. - * Type: ALfloat - * Range: [0.0 - ] - * Default: 1.0 - * - * Scale for source and listener velocities. - */ -#define AL_DOPPLER_FACTOR 0xC000 -AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); - -/** - * Doppler velocity (deprecated). - * - * A multiplier applied to the Speed of Sound. - */ -#define AL_DOPPLER_VELOCITY 0xC001 -AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); - -/** - * Speed of Sound, in units per second. - * Type: ALfloat - * Range: [0.0001 - ] - * Default: 343.3 - * - * The speed at which sound waves are assumed to travel, when calculating the - * doppler effect. - */ -#define AL_SPEED_OF_SOUND 0xC003 -AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); - -/** - * Distance attenuation model. - * Type: ALint - * Range: [AL_NONE, AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED, - * AL_LINEAR_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED, - * AL_EXPONENT_DISTANCE, AL_EXPONENT_DISTANCE_CLAMPED] - * Default: AL_INVERSE_DISTANCE_CLAMPED - * - * The model by which sources attenuate with distance. - * - * None - No distance attenuation. - * Inverse - Doubling the distance halves the source gain. - * Linear - Linear gain scaling between the reference and max distances. - * Exponent - Exponential gain dropoff. - * - * Clamped variations work like the non-clamped counterparts, except the - * distance calculated is clamped between the reference and max distances. - */ -#define AL_DISTANCE_MODEL 0xD000 -AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); - -/** Distance model value. */ -#define AL_INVERSE_DISTANCE 0xD001 -#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 -#define AL_LINEAR_DISTANCE 0xD003 -#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 -#define AL_EXPONENT_DISTANCE 0xD005 -#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 - -/** Renderer State management. */ -AL_API void AL_APIENTRY alEnable(ALenum capability); -AL_API void AL_APIENTRY alDisable(ALenum capability); -AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability); - -/** State retrieval. */ -AL_API const ALchar* AL_APIENTRY alGetString(ALenum param); -AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values); -AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); -AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); -AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param); -AL_API ALint AL_APIENTRY alGetInteger(ALenum param); -AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param); -AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param); - -/** - * Error retrieval. - * - * Obtain the first error generated in the AL context since the last check. - */ -AL_API ALenum AL_APIENTRY alGetError(void); - -/** - * Extension support. - * - * Query for the presence of an extension, and obtain any appropriate function - * pointers and enum values. - */ -AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname); -AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname); -AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename); - - -/** Set Listener parameters */ -AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value); -AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value); -AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values); - -/** Get Listener parameters */ -AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values); - - -/** Create Source objects. */ -AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources); -/** Delete Source objects. */ -AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources); -/** Verify a handle is a valid Source. */ -AL_API ALboolean AL_APIENTRY alIsSource(ALuint source); - -/** Set Source parameters. */ -AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); -AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); - -/** Get Source parameters. */ -AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); - - -/** Play, replay, or resume (if paused) a list of Sources */ -AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); -/** Stop a list of Sources */ -AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); -/** Rewind a list of Sources */ -AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); -/** Pause a list of Sources */ -AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); - -/** Play, replay, or resume a Source */ -AL_API void AL_APIENTRY alSourcePlay(ALuint source); -/** Stop a Source */ -AL_API void AL_APIENTRY alSourceStop(ALuint source); -/** Rewind a Source (set playback postiton to beginning) */ -AL_API void AL_APIENTRY alSourceRewind(ALuint source); -/** Pause a Source */ -AL_API void AL_APIENTRY alSourcePause(ALuint source); - -/** Queue buffers onto a source */ -AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); -/** Unqueue processed buffers from a source */ -AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); - - -/** Create Buffer objects */ -AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers); -/** Delete Buffer objects */ -AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers); -/** Verify a handle is a valid Buffer */ -AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer); - -/** Specifies the data to be copied into a buffer */ -AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); - -/** Set Buffer parameters, */ -AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); -AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); - -/** Get Buffer parameters. */ -AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); - -/** Pointer-to-function type, useful for dynamically getting AL entry points. */ -typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability); -typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability); -typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability); -typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param); -typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values); -typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); -typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param); -typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param); -typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param); -typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param); -typedef ALenum (AL_APIENTRY *LPALGETERROR)(void); -typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname); -typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname); -typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename); -typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources); -typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources); -typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); -typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); -typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers); -typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers); -typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer); -typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); -typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value); -typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value); -typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value); -typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel); - -#if defined(__cplusplus) -} /* extern "C" */ -#endif - -#endif /* AL_AL_H */ diff --git a/release/include/AL/alc.h b/release/include/AL/alc.h deleted file mode 100644 index 294e8b33c..000000000 --- a/release/include/AL/alc.h +++ /dev/null @@ -1,237 +0,0 @@ -#ifndef AL_ALC_H -#define AL_ALC_H - -#if defined(__cplusplus) -extern "C" { -#endif - -#ifndef ALC_API - #if defined(AL_LIBTYPE_STATIC) - #define ALC_API - #elif defined(_WIN32) - #define ALC_API __declspec(dllimport) - #else - #define ALC_API extern - #endif -#endif - -#if defined(_WIN32) - #define ALC_APIENTRY __cdecl -#else - #define ALC_APIENTRY -#endif - - -/** Deprecated macro. */ -#define ALCAPI ALC_API -#define ALCAPIENTRY ALC_APIENTRY -#define ALC_INVALID 0 - -/** Supported ALC version? */ -#define ALC_VERSION_0_1 1 - -/** Opaque device handle */ -typedef struct ALCdevice_struct ALCdevice; -/** Opaque context handle */ -typedef struct ALCcontext_struct ALCcontext; - -/** 8-bit boolean */ -typedef char ALCboolean; - -/** character */ -typedef char ALCchar; - -/** signed 8-bit 2's complement integer */ -typedef signed char ALCbyte; - -/** unsigned 8-bit integer */ -typedef unsigned char ALCubyte; - -/** signed 16-bit 2's complement integer */ -typedef short ALCshort; - -/** unsigned 16-bit integer */ -typedef unsigned short ALCushort; - -/** signed 32-bit 2's complement integer */ -typedef int ALCint; - -/** unsigned 32-bit integer */ -typedef unsigned int ALCuint; - -/** non-negative 32-bit binary integer size */ -typedef int ALCsizei; - -/** enumerated 32-bit value */ -typedef int ALCenum; - -/** 32-bit IEEE754 floating-point */ -typedef float ALCfloat; - -/** 64-bit IEEE754 floating-point */ -typedef double ALCdouble; - -/** void type (for opaque pointers only) */ -typedef void ALCvoid; - - -/* Enumerant values begin at column 50. No tabs. */ - -/** Boolean False. */ -#define ALC_FALSE 0 - -/** Boolean True. */ -#define ALC_TRUE 1 - -/** Context attribute: Hz. */ -#define ALC_FREQUENCY 0x1007 - -/** Context attribute: Hz. */ -#define ALC_REFRESH 0x1008 - -/** Context attribute: AL_TRUE or AL_FALSE. */ -#define ALC_SYNC 0x1009 - -/** Context attribute: requested Mono (3D) Sources. */ -#define ALC_MONO_SOURCES 0x1010 - -/** Context attribute: requested Stereo Sources. */ -#define ALC_STEREO_SOURCES 0x1011 - -/** No error. */ -#define ALC_NO_ERROR 0 - -/** Invalid device handle. */ -#define ALC_INVALID_DEVICE 0xA001 - -/** Invalid context handle. */ -#define ALC_INVALID_CONTEXT 0xA002 - -/** Invalid enum parameter passed to an ALC call. */ -#define ALC_INVALID_ENUM 0xA003 - -/** Invalid value parameter passed to an ALC call. */ -#define ALC_INVALID_VALUE 0xA004 - -/** Out of memory. */ -#define ALC_OUT_OF_MEMORY 0xA005 - - -/** Runtime ALC version. */ -#define ALC_MAJOR_VERSION 0x1000 -#define ALC_MINOR_VERSION 0x1001 - -/** Context attribute list properties. */ -#define ALC_ATTRIBUTES_SIZE 0x1002 -#define ALC_ALL_ATTRIBUTES 0x1003 - -/** String for the default device specifier. */ -#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 -/** - * String for the given device's specifier. - * - * If device handle is NULL, it is instead a null-char separated list of - * strings of known device specifiers (list ends with an empty string). - */ -#define ALC_DEVICE_SPECIFIER 0x1005 -/** String for space-separated list of ALC extensions. */ -#define ALC_EXTENSIONS 0x1006 - - -/** Capture extension */ -#define ALC_EXT_CAPTURE 1 -/** - * String for the given capture device's specifier. - * - * If device handle is NULL, it is instead a null-char separated list of - * strings of known capture device specifiers (list ends with an empty string). - */ -#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 -/** String for the default capture device specifier. */ -#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 -/** Number of sample frames available for capture. */ -#define ALC_CAPTURE_SAMPLES 0x312 - - -/** Enumerate All extension */ -#define ALC_ENUMERATE_ALL_EXT 1 -/** String for the default extended device specifier. */ -#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 -/** - * String for the given extended device's specifier. - * - * If device handle is NULL, it is instead a null-char separated list of - * strings of known extended device specifiers (list ends with an empty string). - */ -#define ALC_ALL_DEVICES_SPECIFIER 0x1013 - - -/** Context management. */ -ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint* attrlist); -ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context); -ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context); -ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context); -ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context); -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void); -ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context); - -/** Device management. */ -ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename); -ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device); - - -/** - * Error support. - * - * Obtain the most recent Device error. - */ -ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); - -/** - * Extension support. - * - * Query for the presence of an extension, and obtain any appropriate - * function pointers and enum values. - */ -ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname); -ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname); -ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname); - -/** Query function. */ -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); -ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); - -/** Capture function. */ -ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); -ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device); -ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device); -ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device); -ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); - -/** Pointer-to-function type, useful for dynamically getting ALC entry points. */ -typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist); -typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void); -typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context); -typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename); -typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device); -typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device); -typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname); -typedef void* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname); -typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname); -typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param); -typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); -typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); -typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); - -#if defined(__cplusplus) -} -#endif - -#endif /* AL_ALC_H */ diff --git a/release/include/AL/alext.h b/release/include/AL/alext.h deleted file mode 100644 index 4b9a15537..000000000 --- a/release/include/AL/alext.h +++ /dev/null @@ -1,466 +0,0 @@ -/** - * OpenAL cross platform audio library - * Copyright (C) 2008 by authors. - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * Or go to http://www.gnu.org/copyleft/lgpl.html - */ - -#ifndef AL_ALEXT_H -#define AL_ALEXT_H - -#include -/* Define int64_t and uint64_t types */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#include -#elif defined(_WIN32) && defined(__GNUC__) -#include -#elif defined(_WIN32) -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -/* Fallback if nothing above works */ -#include -#endif - -#include "alc.h" -#include "al.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef AL_LOKI_IMA_ADPCM_format -#define AL_LOKI_IMA_ADPCM_format 1 -#define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 -#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001 -#endif - -#ifndef AL_LOKI_WAVE_format -#define AL_LOKI_WAVE_format 1 -#define AL_FORMAT_WAVE_EXT 0x10002 -#endif - -#ifndef AL_EXT_vorbis -#define AL_EXT_vorbis 1 -#define AL_FORMAT_VORBIS_EXT 0x10003 -#endif - -#ifndef AL_LOKI_quadriphonic -#define AL_LOKI_quadriphonic 1 -#define AL_FORMAT_QUAD8_LOKI 0x10004 -#define AL_FORMAT_QUAD16_LOKI 0x10005 -#endif - -#ifndef AL_EXT_float32 -#define AL_EXT_float32 1 -#define AL_FORMAT_MONO_FLOAT32 0x10010 -#define AL_FORMAT_STEREO_FLOAT32 0x10011 -#endif - -#ifndef AL_EXT_double -#define AL_EXT_double 1 -#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012 -#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013 -#endif - -#ifndef AL_EXT_MULAW -#define AL_EXT_MULAW 1 -#define AL_FORMAT_MONO_MULAW_EXT 0x10014 -#define AL_FORMAT_STEREO_MULAW_EXT 0x10015 -#endif - -#ifndef AL_EXT_ALAW -#define AL_EXT_ALAW 1 -#define AL_FORMAT_MONO_ALAW_EXT 0x10016 -#define AL_FORMAT_STEREO_ALAW_EXT 0x10017 -#endif - -#ifndef ALC_LOKI_audio_channel -#define ALC_LOKI_audio_channel 1 -#define ALC_CHAN_MAIN_LOKI 0x500001 -#define ALC_CHAN_PCM_LOKI 0x500002 -#define ALC_CHAN_CD_LOKI 0x500003 -#endif - -#ifndef AL_EXT_MCFORMATS -#define AL_EXT_MCFORMATS 1 -#define AL_FORMAT_QUAD8 0x1204 -#define AL_FORMAT_QUAD16 0x1205 -#define AL_FORMAT_QUAD32 0x1206 -#define AL_FORMAT_REAR8 0x1207 -#define AL_FORMAT_REAR16 0x1208 -#define AL_FORMAT_REAR32 0x1209 -#define AL_FORMAT_51CHN8 0x120A -#define AL_FORMAT_51CHN16 0x120B -#define AL_FORMAT_51CHN32 0x120C -#define AL_FORMAT_61CHN8 0x120D -#define AL_FORMAT_61CHN16 0x120E -#define AL_FORMAT_61CHN32 0x120F -#define AL_FORMAT_71CHN8 0x1210 -#define AL_FORMAT_71CHN16 0x1211 -#define AL_FORMAT_71CHN32 0x1212 -#endif - -#ifndef AL_EXT_MULAW_MCFORMATS -#define AL_EXT_MULAW_MCFORMATS 1 -#define AL_FORMAT_MONO_MULAW 0x10014 -#define AL_FORMAT_STEREO_MULAW 0x10015 -#define AL_FORMAT_QUAD_MULAW 0x10021 -#define AL_FORMAT_REAR_MULAW 0x10022 -#define AL_FORMAT_51CHN_MULAW 0x10023 -#define AL_FORMAT_61CHN_MULAW 0x10024 -#define AL_FORMAT_71CHN_MULAW 0x10025 -#endif - -#ifndef AL_EXT_IMA4 -#define AL_EXT_IMA4 1 -#define AL_FORMAT_MONO_IMA4 0x1300 -#define AL_FORMAT_STEREO_IMA4 0x1301 -#endif - -#ifndef AL_EXT_STATIC_BUFFER -#define AL_EXT_STATIC_BUFFER 1 -typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei); -#ifdef AL_ALEXT_PROTOTYPES -AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq); -#endif -#endif - -#ifndef ALC_EXT_EFX -#define ALC_EXT_EFX 1 -#include "efx.h" -#endif - -#ifndef ALC_EXT_disconnect -#define ALC_EXT_disconnect 1 -#define ALC_CONNECTED 0x313 -#endif - -#ifndef ALC_EXT_thread_local_context -#define ALC_EXT_thread_local_context 1 -typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); -#ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); -#endif -#endif - -#ifndef AL_EXT_source_distance_model -#define AL_EXT_source_distance_model 1 -#define AL_SOURCE_DISTANCE_MODEL 0x200 -#endif - -#ifndef AL_SOFT_buffer_sub_data -#define AL_SOFT_buffer_sub_data 1 -#define AL_BYTE_RW_OFFSETS_SOFT 0x1031 -#define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 -typedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); -#ifdef AL_ALEXT_PROTOTYPES -AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); -#endif -#endif - -#ifndef AL_SOFT_loop_points -#define AL_SOFT_loop_points 1 -#define AL_LOOP_POINTS_SOFT 0x2015 -#endif - -#ifndef AL_EXT_FOLDBACK -#define AL_EXT_FOLDBACK 1 -#define AL_EXT_FOLDBACK_NAME "AL_EXT_FOLDBACK" -#define AL_FOLDBACK_EVENT_BLOCK 0x4112 -#define AL_FOLDBACK_EVENT_START 0x4111 -#define AL_FOLDBACK_EVENT_STOP 0x4113 -#define AL_FOLDBACK_MODE_MONO 0x4101 -#define AL_FOLDBACK_MODE_STEREO 0x4102 -typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); -#ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); -AL_API void AL_APIENTRY alRequestFoldbackStop(void); -#endif -#endif - -#ifndef ALC_EXT_DEDICATED -#define ALC_EXT_DEDICATED 1 -#define AL_DEDICATED_GAIN 0x0001 -#define AL_EFFECT_DEDICATED_DIALOGUE 0x9001 -#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000 -#endif - -#ifndef AL_SOFT_buffer_samples -#define AL_SOFT_buffer_samples 1 -/* Channel configurations */ -#define AL_MONO_SOFT 0x1500 -#define AL_STEREO_SOFT 0x1501 -#define AL_REAR_SOFT 0x1502 -#define AL_QUAD_SOFT 0x1503 -#define AL_5POINT1_SOFT 0x1504 -#define AL_6POINT1_SOFT 0x1505 -#define AL_7POINT1_SOFT 0x1506 - -/* Sample types */ -#define AL_BYTE_SOFT 0x1400 -#define AL_UNSIGNED_BYTE_SOFT 0x1401 -#define AL_SHORT_SOFT 0x1402 -#define AL_UNSIGNED_SHORT_SOFT 0x1403 -#define AL_INT_SOFT 0x1404 -#define AL_UNSIGNED_INT_SOFT 0x1405 -#define AL_FLOAT_SOFT 0x1406 -#define AL_DOUBLE_SOFT 0x1407 -#define AL_BYTE3_SOFT 0x1408 -#define AL_UNSIGNED_BYTE3_SOFT 0x1409 - -/* Storage formats */ -#define AL_MONO8_SOFT 0x1100 -#define AL_MONO16_SOFT 0x1101 -#define AL_MONO32F_SOFT 0x10010 -#define AL_STEREO8_SOFT 0x1102 -#define AL_STEREO16_SOFT 0x1103 -#define AL_STEREO32F_SOFT 0x10011 -#define AL_QUAD8_SOFT 0x1204 -#define AL_QUAD16_SOFT 0x1205 -#define AL_QUAD32F_SOFT 0x1206 -#define AL_REAR8_SOFT 0x1207 -#define AL_REAR16_SOFT 0x1208 -#define AL_REAR32F_SOFT 0x1209 -#define AL_5POINT1_8_SOFT 0x120A -#define AL_5POINT1_16_SOFT 0x120B -#define AL_5POINT1_32F_SOFT 0x120C -#define AL_6POINT1_8_SOFT 0x120D -#define AL_6POINT1_16_SOFT 0x120E -#define AL_6POINT1_32F_SOFT 0x120F -#define AL_7POINT1_8_SOFT 0x1210 -#define AL_7POINT1_16_SOFT 0x1211 -#define AL_7POINT1_32F_SOFT 0x1212 - -/* Buffer attributes */ -#define AL_INTERNAL_FORMAT_SOFT 0x2008 -#define AL_BYTE_LENGTH_SOFT 0x2009 -#define AL_SAMPLE_LENGTH_SOFT 0x200A -#define AL_SEC_LENGTH_SOFT 0x200B - -typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); -typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); -#ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); -#endif -#endif - -#ifndef AL_SOFT_direct_channels -#define AL_SOFT_direct_channels 1 -#define AL_DIRECT_CHANNELS_SOFT 0x1033 -#endif - -#ifndef ALC_SOFT_loopback -#define ALC_SOFT_loopback 1 -#define ALC_FORMAT_CHANNELS_SOFT 0x1990 -#define ALC_FORMAT_TYPE_SOFT 0x1991 - -/* Sample types */ -#define ALC_BYTE_SOFT 0x1400 -#define ALC_UNSIGNED_BYTE_SOFT 0x1401 -#define ALC_SHORT_SOFT 0x1402 -#define ALC_UNSIGNED_SHORT_SOFT 0x1403 -#define ALC_INT_SOFT 0x1404 -#define ALC_UNSIGNED_INT_SOFT 0x1405 -#define ALC_FLOAT_SOFT 0x1406 - -/* Channel configurations */ -#define ALC_MONO_SOFT 0x1500 -#define ALC_STEREO_SOFT 0x1501 -#define ALC_QUAD_SOFT 0x1503 -#define ALC_5POINT1_SOFT 0x1504 -#define ALC_6POINT1_SOFT 0x1505 -#define ALC_7POINT1_SOFT 0x1506 - -typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); -typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); -typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); -#ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); -ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); -ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); -#endif -#endif - -#ifndef AL_EXT_STEREO_ANGLES -#define AL_EXT_STEREO_ANGLES 1 -#define AL_STEREO_ANGLES 0x1030 -#endif - -#ifndef AL_EXT_SOURCE_RADIUS -#define AL_EXT_SOURCE_RADIUS 1 -#define AL_SOURCE_RADIUS 0x1031 -#endif - -#ifndef AL_SOFT_source_latency -#define AL_SOFT_source_latency 1 -#define AL_SAMPLE_OFFSET_LATENCY_SOFT 0x1200 -#define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 -typedef int64_t ALint64SOFT; -typedef uint64_t ALuint64SOFT; -typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); -#ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); -AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); -AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); -AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); -AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); -AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); -AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); -AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); -AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); -AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); -AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); -AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); -#endif -#endif - -#ifndef ALC_EXT_DEFAULT_FILTER_ORDER -#define ALC_EXT_DEFAULT_FILTER_ORDER 1 -#define ALC_DEFAULT_FILTER_ORDER 0x1100 -#endif - -#ifndef AL_SOFT_deferred_updates -#define AL_SOFT_deferred_updates 1 -#define AL_DEFERRED_UPDATES_SOFT 0xC002 -typedef ALvoid (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void); -typedef ALvoid (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void); -#ifdef AL_ALEXT_PROTOTYPES -AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void); -AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void); -#endif -#endif - -#ifndef AL_SOFT_block_alignment -#define AL_SOFT_block_alignment 1 -#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C -#define AL_PACK_BLOCK_ALIGNMENT_SOFT 0x200D -#endif - -#ifndef AL_SOFT_MSADPCM -#define AL_SOFT_MSADPCM 1 -#define AL_FORMAT_MONO_MSADPCM_SOFT 0x1302 -#define AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303 -#endif - -#ifndef AL_SOFT_source_length -#define AL_SOFT_source_length 1 -/*#define AL_BYTE_LENGTH_SOFT 0x2009*/ -/*#define AL_SAMPLE_LENGTH_SOFT 0x200A*/ -/*#define AL_SEC_LENGTH_SOFT 0x200B*/ -#endif - -#ifndef ALC_SOFT_pause_device -#define ALC_SOFT_pause_device 1 -typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device); -typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device); -#ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device); -ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); -#endif -#endif - -#ifndef AL_EXT_BFORMAT -#define AL_EXT_BFORMAT 1 -#define AL_FORMAT_BFORMAT2D_8 0x20021 -#define AL_FORMAT_BFORMAT2D_16 0x20022 -#define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023 -#define AL_FORMAT_BFORMAT3D_8 0x20031 -#define AL_FORMAT_BFORMAT3D_16 0x20032 -#define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033 -#endif - -#ifndef AL_EXT_MULAW_BFORMAT -#define AL_EXT_MULAW_BFORMAT 1 -#define AL_FORMAT_BFORMAT2D_MULAW 0x10031 -#define AL_FORMAT_BFORMAT3D_MULAW 0x10032 -#endif - -#ifndef ALC_SOFT_HRTF -#define ALC_SOFT_HRTF 1 -#define ALC_HRTF_SOFT 0x1992 -#define ALC_DONT_CARE_SOFT 0x0002 -#define ALC_HRTF_STATUS_SOFT 0x1993 -#define ALC_HRTF_DISABLED_SOFT 0x0000 -#define ALC_HRTF_ENABLED_SOFT 0x0001 -#define ALC_HRTF_DENIED_SOFT 0x0002 -#define ALC_HRTF_REQUIRED_SOFT 0x0003 -#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004 -#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005 -#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 -#define ALC_HRTF_SPECIFIER_SOFT 0x1995 -#define ALC_HRTF_ID_SOFT 0x1996 -typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); -typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); -#ifdef AL_ALEXT_PROTOTYPES -ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); -ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); -#endif -#endif - -#ifndef AL_SOFT_gain_clamp_ex -#define AL_SOFT_gain_clamp_ex 1 -#define AL_GAIN_LIMIT_SOFT 0x200E -#endif - -#ifndef AL_SOFT_source_resampler -#define AL_SOFT_source_resampler -#define AL_NUM_RESAMPLERS_SOFT 0x1210 -#define AL_DEFAULT_RESAMPLER_SOFT 0x1211 -#define AL_SOURCE_RESAMPLER_SOFT 0x1212 -#define AL_RESAMPLER_NAME_SOFT 0x1213 -typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index); -#ifdef AL_ALEXT_PROTOTYPES -AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index); -#endif -#endif - -#ifndef AL_SOFT_source_spatialize -#define AL_SOFT_source_spatialize -#define AL_SOURCE_SPATIALIZE_SOFT 0x1214 -#define AL_AUTO_SOFT 0x0002 -#endif - -#ifndef ALC_SOFT_output_limiter -#define ALC_SOFT_output_limiter -#define ALC_OUTPUT_LIMITER_SOFT 0x199A -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/release/include/AL/efx-creative.h b/release/include/AL/efx-creative.h deleted file mode 100644 index 0a04c982e..000000000 --- a/release/include/AL/efx-creative.h +++ /dev/null @@ -1,3 +0,0 @@ -/* The tokens that would be defined here are already defined in efx.h. This - * empty file is here to provide compatibility with Windows-based projects - * that would include it. */ diff --git a/release/include/AL/efx-presets.h b/release/include/AL/efx-presets.h deleted file mode 100644 index 8539fd517..000000000 --- a/release/include/AL/efx-presets.h +++ /dev/null @@ -1,402 +0,0 @@ -/* Reverb presets for EFX */ - -#ifndef EFX_PRESETS_H -#define EFX_PRESETS_H - -#ifndef EFXEAXREVERBPROPERTIES_DEFINED -#define EFXEAXREVERBPROPERTIES_DEFINED -typedef struct { - float flDensity; - float flDiffusion; - float flGain; - float flGainHF; - float flGainLF; - float flDecayTime; - float flDecayHFRatio; - float flDecayLFRatio; - float flReflectionsGain; - float flReflectionsDelay; - float flReflectionsPan[3]; - float flLateReverbGain; - float flLateReverbDelay; - float flLateReverbPan[3]; - float flEchoTime; - float flEchoDepth; - float flModulationTime; - float flModulationDepth; - float flAirAbsorptionGainHF; - float flHFReference; - float flLFReference; - float flRoomRolloffFactor; - int iDecayHFLimit; -} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES; -#endif - -/* Default Presets */ - -#define EFX_REVERB_PRESET_GENERIC \ - { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PADDEDCELL \ - { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ROOM \ - { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_BATHROOM \ - { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_LIVINGROOM \ - { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_STONEROOM \ - { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_AUDITORIUM \ - { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CONCERTHALL \ - { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CAVE \ - { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_ARENA \ - { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_HANGAR \ - { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CARPETEDHALLWAY \ - { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_HALLWAY \ - { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_STONECORRIDOR \ - { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ALLEY \ - { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FOREST \ - { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CITY \ - { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_MOUNTAINS \ - { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_QUARRY \ - { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PLAIN \ - { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PARKINGLOT \ - { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_SEWERPIPE \ - { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_UNDERWATER \ - { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_DRUGGED \ - { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_DIZZY \ - { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_PSYCHOTIC \ - { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -/* Castle Presets */ - -#define EFX_REVERB_PRESET_CASTLE_SMALLROOM \ - { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \ - { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \ - { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_LARGEROOM \ - { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \ - { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_HALL \ - { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_CUPBOARD \ - { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CASTLE_COURTYARD \ - { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_CASTLE_ALCOVE \ - { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } - -/* Factory Presets */ - -#define EFX_REVERB_PRESET_FACTORY_SMALLROOM \ - { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \ - { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \ - { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_LARGEROOM \ - { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \ - { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_HALL \ - { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_CUPBOARD \ - { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_COURTYARD \ - { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_FACTORY_ALCOVE \ - { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } - -/* Ice Palace Presets */ - -#define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \ - { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \ - { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \ - { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \ - { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \ - { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_HALL \ - { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \ - { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \ - { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \ - { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } - -/* Space Station Presets */ - -#define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \ - { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \ - { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \ - { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \ - { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \ - { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_HALL \ - { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \ - { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \ - { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } - -/* Wooden Galleon Presets */ - -#define EFX_REVERB_PRESET_WOODEN_SMALLROOM \ - { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \ - { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \ - { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_LARGEROOM \ - { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \ - { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_HALL \ - { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_CUPBOARD \ - { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_COURTYARD \ - { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_WOODEN_ALCOVE \ - { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } - -/* Sports Presets */ - -#define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \ - { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \ - { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \ - { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \ - { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_SPORT_GYMNASIUM \ - { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \ - { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \ - { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -/* Prefab Presets */ - -#define EFX_REVERB_PRESET_PREFAB_WORKSHOP \ - { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \ - { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \ - { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \ - { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_PREFAB_CARAVAN \ - { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -/* Dome and Pipe Presets */ - -#define EFX_REVERB_PRESET_DOME_TOMB \ - { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_PIPE_SMALL \ - { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_DOME_SAINTPAULS \ - { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PIPE_LONGTHIN \ - { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_PIPE_LARGE \ - { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_PIPE_RESONANT \ - { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } - -/* Outdoors Presets */ - -#define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \ - { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \ - { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \ - { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_OUTDOORS_CREEK \ - { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_OUTDOORS_VALLEY \ - { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } - -/* Mood Presets */ - -#define EFX_REVERB_PRESET_MOOD_HEAVEN \ - { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_MOOD_HELL \ - { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_MOOD_MEMORY \ - { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -/* Driving Presets */ - -#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \ - { 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \ - { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \ - { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \ - { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \ - { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \ - { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \ - { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_DRIVING_TUNNEL \ - { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 } - -/* City Presets */ - -#define EFX_REVERB_PRESET_CITY_STREETS \ - { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CITY_SUBWAY \ - { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CITY_MUSEUM \ - { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_CITY_LIBRARY \ - { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } - -#define EFX_REVERB_PRESET_CITY_UNDERPASS \ - { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CITY_ABANDONED \ - { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -/* Misc. Presets */ - -#define EFX_REVERB_PRESET_DUSTYROOM \ - { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_CHAPEL \ - { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } - -#define EFX_REVERB_PRESET_SMALLWATERROOM \ - { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } - -#endif /* EFX_PRESETS_H */ diff --git a/release/include/AL/efx.h b/release/include/AL/efx.h deleted file mode 100644 index 57766983f..000000000 --- a/release/include/AL/efx.h +++ /dev/null @@ -1,761 +0,0 @@ -#ifndef AL_EFX_H -#define AL_EFX_H - - -#include "alc.h" -#include "al.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define ALC_EXT_EFX_NAME "ALC_EXT_EFX" - -#define ALC_EFX_MAJOR_VERSION 0x20001 -#define ALC_EFX_MINOR_VERSION 0x20002 -#define ALC_MAX_AUXILIARY_SENDS 0x20003 - - -/* Listener properties. */ -#define AL_METERS_PER_UNIT 0x20004 - -/* Source properties. */ -#define AL_DIRECT_FILTER 0x20005 -#define AL_AUXILIARY_SEND_FILTER 0x20006 -#define AL_AIR_ABSORPTION_FACTOR 0x20007 -#define AL_ROOM_ROLLOFF_FACTOR 0x20008 -#define AL_CONE_OUTER_GAINHF 0x20009 -#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A -#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B -#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C - - -/* Effect properties. */ - -/* Reverb effect parameters */ -#define AL_REVERB_DENSITY 0x0001 -#define AL_REVERB_DIFFUSION 0x0002 -#define AL_REVERB_GAIN 0x0003 -#define AL_REVERB_GAINHF 0x0004 -#define AL_REVERB_DECAY_TIME 0x0005 -#define AL_REVERB_DECAY_HFRATIO 0x0006 -#define AL_REVERB_REFLECTIONS_GAIN 0x0007 -#define AL_REVERB_REFLECTIONS_DELAY 0x0008 -#define AL_REVERB_LATE_REVERB_GAIN 0x0009 -#define AL_REVERB_LATE_REVERB_DELAY 0x000A -#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B -#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C -#define AL_REVERB_DECAY_HFLIMIT 0x000D - -/* EAX Reverb effect parameters */ -#define AL_EAXREVERB_DENSITY 0x0001 -#define AL_EAXREVERB_DIFFUSION 0x0002 -#define AL_EAXREVERB_GAIN 0x0003 -#define AL_EAXREVERB_GAINHF 0x0004 -#define AL_EAXREVERB_GAINLF 0x0005 -#define AL_EAXREVERB_DECAY_TIME 0x0006 -#define AL_EAXREVERB_DECAY_HFRATIO 0x0007 -#define AL_EAXREVERB_DECAY_LFRATIO 0x0008 -#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009 -#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A -#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B -#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C -#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D -#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E -#define AL_EAXREVERB_ECHO_TIME 0x000F -#define AL_EAXREVERB_ECHO_DEPTH 0x0010 -#define AL_EAXREVERB_MODULATION_TIME 0x0011 -#define AL_EAXREVERB_MODULATION_DEPTH 0x0012 -#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013 -#define AL_EAXREVERB_HFREFERENCE 0x0014 -#define AL_EAXREVERB_LFREFERENCE 0x0015 -#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 -#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 - -/* Chorus effect parameters */ -#define AL_CHORUS_WAVEFORM 0x0001 -#define AL_CHORUS_PHASE 0x0002 -#define AL_CHORUS_RATE 0x0003 -#define AL_CHORUS_DEPTH 0x0004 -#define AL_CHORUS_FEEDBACK 0x0005 -#define AL_CHORUS_DELAY 0x0006 - -/* Distortion effect parameters */ -#define AL_DISTORTION_EDGE 0x0001 -#define AL_DISTORTION_GAIN 0x0002 -#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003 -#define AL_DISTORTION_EQCENTER 0x0004 -#define AL_DISTORTION_EQBANDWIDTH 0x0005 - -/* Echo effect parameters */ -#define AL_ECHO_DELAY 0x0001 -#define AL_ECHO_LRDELAY 0x0002 -#define AL_ECHO_DAMPING 0x0003 -#define AL_ECHO_FEEDBACK 0x0004 -#define AL_ECHO_SPREAD 0x0005 - -/* Flanger effect parameters */ -#define AL_FLANGER_WAVEFORM 0x0001 -#define AL_FLANGER_PHASE 0x0002 -#define AL_FLANGER_RATE 0x0003 -#define AL_FLANGER_DEPTH 0x0004 -#define AL_FLANGER_FEEDBACK 0x0005 -#define AL_FLANGER_DELAY 0x0006 - -/* Frequency shifter effect parameters */ -#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001 -#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002 -#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003 - -/* Vocal morpher effect parameters */ -#define AL_VOCAL_MORPHER_PHONEMEA 0x0001 -#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002 -#define AL_VOCAL_MORPHER_PHONEMEB 0x0003 -#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004 -#define AL_VOCAL_MORPHER_WAVEFORM 0x0005 -#define AL_VOCAL_MORPHER_RATE 0x0006 - -/* Pitchshifter effect parameters */ -#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001 -#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002 - -/* Ringmodulator effect parameters */ -#define AL_RING_MODULATOR_FREQUENCY 0x0001 -#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002 -#define AL_RING_MODULATOR_WAVEFORM 0x0003 - -/* Autowah effect parameters */ -#define AL_AUTOWAH_ATTACK_TIME 0x0001 -#define AL_AUTOWAH_RELEASE_TIME 0x0002 -#define AL_AUTOWAH_RESONANCE 0x0003 -#define AL_AUTOWAH_PEAK_GAIN 0x0004 - -/* Compressor effect parameters */ -#define AL_COMPRESSOR_ONOFF 0x0001 - -/* Equalizer effect parameters */ -#define AL_EQUALIZER_LOW_GAIN 0x0001 -#define AL_EQUALIZER_LOW_CUTOFF 0x0002 -#define AL_EQUALIZER_MID1_GAIN 0x0003 -#define AL_EQUALIZER_MID1_CENTER 0x0004 -#define AL_EQUALIZER_MID1_WIDTH 0x0005 -#define AL_EQUALIZER_MID2_GAIN 0x0006 -#define AL_EQUALIZER_MID2_CENTER 0x0007 -#define AL_EQUALIZER_MID2_WIDTH 0x0008 -#define AL_EQUALIZER_HIGH_GAIN 0x0009 -#define AL_EQUALIZER_HIGH_CUTOFF 0x000A - -/* Effect type */ -#define AL_EFFECT_FIRST_PARAMETER 0x0000 -#define AL_EFFECT_LAST_PARAMETER 0x8000 -#define AL_EFFECT_TYPE 0x8001 - -/* Effect types, used with the AL_EFFECT_TYPE property */ -#define AL_EFFECT_NULL 0x0000 -#define AL_EFFECT_REVERB 0x0001 -#define AL_EFFECT_CHORUS 0x0002 -#define AL_EFFECT_DISTORTION 0x0003 -#define AL_EFFECT_ECHO 0x0004 -#define AL_EFFECT_FLANGER 0x0005 -#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 -#define AL_EFFECT_VOCAL_MORPHER 0x0007 -#define AL_EFFECT_PITCH_SHIFTER 0x0008 -#define AL_EFFECT_RING_MODULATOR 0x0009 -#define AL_EFFECT_AUTOWAH 0x000A -#define AL_EFFECT_COMPRESSOR 0x000B -#define AL_EFFECT_EQUALIZER 0x000C -#define AL_EFFECT_EAXREVERB 0x8000 - -/* Auxiliary Effect Slot properties. */ -#define AL_EFFECTSLOT_EFFECT 0x0001 -#define AL_EFFECTSLOT_GAIN 0x0002 -#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003 - -/* NULL Auxiliary Slot ID to disable a source send. */ -#define AL_EFFECTSLOT_NULL 0x0000 - - -/* Filter properties. */ - -/* Lowpass filter parameters */ -#define AL_LOWPASS_GAIN 0x0001 -#define AL_LOWPASS_GAINHF 0x0002 - -/* Highpass filter parameters */ -#define AL_HIGHPASS_GAIN 0x0001 -#define AL_HIGHPASS_GAINLF 0x0002 - -/* Bandpass filter parameters */ -#define AL_BANDPASS_GAIN 0x0001 -#define AL_BANDPASS_GAINLF 0x0002 -#define AL_BANDPASS_GAINHF 0x0003 - -/* Filter type */ -#define AL_FILTER_FIRST_PARAMETER 0x0000 -#define AL_FILTER_LAST_PARAMETER 0x8000 -#define AL_FILTER_TYPE 0x8001 - -/* Filter types, used with the AL_FILTER_TYPE property */ -#define AL_FILTER_NULL 0x0000 -#define AL_FILTER_LOWPASS 0x0001 -#define AL_FILTER_HIGHPASS 0x0002 -#define AL_FILTER_BANDPASS 0x0003 - - -/* Effect object function types. */ -typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); -typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); - -/* Filter object function types. */ -typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); -typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); - -/* Auxiliary Effect Slot object function types. */ -typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); - -#ifdef AL_ALEXT_PROTOTYPES -AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); -AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects); -AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); -AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); -AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues); -AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); -AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues); -AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); -AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); -AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); -AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); - -AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); -AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters); -AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); -AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); -AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues); -AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); -AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues); -AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); -AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); -AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); -AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); - -AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); -AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots); -AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); -AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); -AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues); -AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); -AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues); -AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); -AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); -AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); -AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); -#endif - -/* Filter ranges and defaults. */ - -/* Lowpass filter */ -#define AL_LOWPASS_MIN_GAIN (0.0f) -#define AL_LOWPASS_MAX_GAIN (1.0f) -#define AL_LOWPASS_DEFAULT_GAIN (1.0f) - -#define AL_LOWPASS_MIN_GAINHF (0.0f) -#define AL_LOWPASS_MAX_GAINHF (1.0f) -#define AL_LOWPASS_DEFAULT_GAINHF (1.0f) - -/* Highpass filter */ -#define AL_HIGHPASS_MIN_GAIN (0.0f) -#define AL_HIGHPASS_MAX_GAIN (1.0f) -#define AL_HIGHPASS_DEFAULT_GAIN (1.0f) - -#define AL_HIGHPASS_MIN_GAINLF (0.0f) -#define AL_HIGHPASS_MAX_GAINLF (1.0f) -#define AL_HIGHPASS_DEFAULT_GAINLF (1.0f) - -/* Bandpass filter */ -#define AL_BANDPASS_MIN_GAIN (0.0f) -#define AL_BANDPASS_MAX_GAIN (1.0f) -#define AL_BANDPASS_DEFAULT_GAIN (1.0f) - -#define AL_BANDPASS_MIN_GAINHF (0.0f) -#define AL_BANDPASS_MAX_GAINHF (1.0f) -#define AL_BANDPASS_DEFAULT_GAINHF (1.0f) - -#define AL_BANDPASS_MIN_GAINLF (0.0f) -#define AL_BANDPASS_MAX_GAINLF (1.0f) -#define AL_BANDPASS_DEFAULT_GAINLF (1.0f) - - -/* Effect parameter ranges and defaults. */ - -/* Standard reverb effect */ -#define AL_REVERB_MIN_DENSITY (0.0f) -#define AL_REVERB_MAX_DENSITY (1.0f) -#define AL_REVERB_DEFAULT_DENSITY (1.0f) - -#define AL_REVERB_MIN_DIFFUSION (0.0f) -#define AL_REVERB_MAX_DIFFUSION (1.0f) -#define AL_REVERB_DEFAULT_DIFFUSION (1.0f) - -#define AL_REVERB_MIN_GAIN (0.0f) -#define AL_REVERB_MAX_GAIN (1.0f) -#define AL_REVERB_DEFAULT_GAIN (0.32f) - -#define AL_REVERB_MIN_GAINHF (0.0f) -#define AL_REVERB_MAX_GAINHF (1.0f) -#define AL_REVERB_DEFAULT_GAINHF (0.89f) - -#define AL_REVERB_MIN_DECAY_TIME (0.1f) -#define AL_REVERB_MAX_DECAY_TIME (20.0f) -#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f) - -#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f) -#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f) -#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f) - -#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f) -#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f) -#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) - -#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f) -#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f) -#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) - -#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f) -#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f) -#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) - -#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f) -#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f) -#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) - -#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) -#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) -#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) - -#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) -#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) -#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) - -#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE -#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE -#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE - -/* EAX reverb effect */ -#define AL_EAXREVERB_MIN_DENSITY (0.0f) -#define AL_EAXREVERB_MAX_DENSITY (1.0f) -#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f) - -#define AL_EAXREVERB_MIN_DIFFUSION (0.0f) -#define AL_EAXREVERB_MAX_DIFFUSION (1.0f) -#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f) - -#define AL_EAXREVERB_MIN_GAIN (0.0f) -#define AL_EAXREVERB_MAX_GAIN (1.0f) -#define AL_EAXREVERB_DEFAULT_GAIN (0.32f) - -#define AL_EAXREVERB_MIN_GAINHF (0.0f) -#define AL_EAXREVERB_MAX_GAINHF (1.0f) -#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f) - -#define AL_EAXREVERB_MIN_GAINLF (0.0f) -#define AL_EAXREVERB_MAX_GAINLF (1.0f) -#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f) - -#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f) -#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f) -#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f) - -#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f) -#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f) -#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f) - -#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f) -#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f) -#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f) - -#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f) -#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f) -#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) - -#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f) -#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f) -#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) - -#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f) - -#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f) -#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f) -#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) - -#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f) -#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f) -#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) - -#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f) - -#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f) -#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f) -#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f) - -#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f) -#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f) -#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f) - -#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f) -#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f) -#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f) - -#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f) -#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f) -#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f) - -#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) -#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) -#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) - -#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f) -#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f) -#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f) - -#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f) -#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f) -#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f) - -#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) -#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) -#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) - -#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE -#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE -#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE - -/* Chorus effect */ -#define AL_CHORUS_WAVEFORM_SINUSOID (0) -#define AL_CHORUS_WAVEFORM_TRIANGLE (1) - -#define AL_CHORUS_MIN_WAVEFORM (0) -#define AL_CHORUS_MAX_WAVEFORM (1) -#define AL_CHORUS_DEFAULT_WAVEFORM (1) - -#define AL_CHORUS_MIN_PHASE (-180) -#define AL_CHORUS_MAX_PHASE (180) -#define AL_CHORUS_DEFAULT_PHASE (90) - -#define AL_CHORUS_MIN_RATE (0.0f) -#define AL_CHORUS_MAX_RATE (10.0f) -#define AL_CHORUS_DEFAULT_RATE (1.1f) - -#define AL_CHORUS_MIN_DEPTH (0.0f) -#define AL_CHORUS_MAX_DEPTH (1.0f) -#define AL_CHORUS_DEFAULT_DEPTH (0.1f) - -#define AL_CHORUS_MIN_FEEDBACK (-1.0f) -#define AL_CHORUS_MAX_FEEDBACK (1.0f) -#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f) - -#define AL_CHORUS_MIN_DELAY (0.0f) -#define AL_CHORUS_MAX_DELAY (0.016f) -#define AL_CHORUS_DEFAULT_DELAY (0.016f) - -/* Distortion effect */ -#define AL_DISTORTION_MIN_EDGE (0.0f) -#define AL_DISTORTION_MAX_EDGE (1.0f) -#define AL_DISTORTION_DEFAULT_EDGE (0.2f) - -#define AL_DISTORTION_MIN_GAIN (0.01f) -#define AL_DISTORTION_MAX_GAIN (1.0f) -#define AL_DISTORTION_DEFAULT_GAIN (0.05f) - -#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f) -#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f) -#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f) - -#define AL_DISTORTION_MIN_EQCENTER (80.0f) -#define AL_DISTORTION_MAX_EQCENTER (24000.0f) -#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f) - -#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f) -#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f) -#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f) - -/* Echo effect */ -#define AL_ECHO_MIN_DELAY (0.0f) -#define AL_ECHO_MAX_DELAY (0.207f) -#define AL_ECHO_DEFAULT_DELAY (0.1f) - -#define AL_ECHO_MIN_LRDELAY (0.0f) -#define AL_ECHO_MAX_LRDELAY (0.404f) -#define AL_ECHO_DEFAULT_LRDELAY (0.1f) - -#define AL_ECHO_MIN_DAMPING (0.0f) -#define AL_ECHO_MAX_DAMPING (0.99f) -#define AL_ECHO_DEFAULT_DAMPING (0.5f) - -#define AL_ECHO_MIN_FEEDBACK (0.0f) -#define AL_ECHO_MAX_FEEDBACK (1.0f) -#define AL_ECHO_DEFAULT_FEEDBACK (0.5f) - -#define AL_ECHO_MIN_SPREAD (-1.0f) -#define AL_ECHO_MAX_SPREAD (1.0f) -#define AL_ECHO_DEFAULT_SPREAD (-1.0f) - -/* Flanger effect */ -#define AL_FLANGER_WAVEFORM_SINUSOID (0) -#define AL_FLANGER_WAVEFORM_TRIANGLE (1) - -#define AL_FLANGER_MIN_WAVEFORM (0) -#define AL_FLANGER_MAX_WAVEFORM (1) -#define AL_FLANGER_DEFAULT_WAVEFORM (1) - -#define AL_FLANGER_MIN_PHASE (-180) -#define AL_FLANGER_MAX_PHASE (180) -#define AL_FLANGER_DEFAULT_PHASE (0) - -#define AL_FLANGER_MIN_RATE (0.0f) -#define AL_FLANGER_MAX_RATE (10.0f) -#define AL_FLANGER_DEFAULT_RATE (0.27f) - -#define AL_FLANGER_MIN_DEPTH (0.0f) -#define AL_FLANGER_MAX_DEPTH (1.0f) -#define AL_FLANGER_DEFAULT_DEPTH (1.0f) - -#define AL_FLANGER_MIN_FEEDBACK (-1.0f) -#define AL_FLANGER_MAX_FEEDBACK (1.0f) -#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f) - -#define AL_FLANGER_MIN_DELAY (0.0f) -#define AL_FLANGER_MAX_DELAY (0.004f) -#define AL_FLANGER_DEFAULT_DELAY (0.002f) - -/* Frequency shifter effect */ -#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f) -#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f) -#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f) - -#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0) -#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2) -#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0) - -#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0) -#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1) -#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2) - -#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0) -#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2) -#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0) - -/* Vocal morpher effect */ -#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0) -#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29) -#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0) - -#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24) -#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24) -#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0) - -#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0) -#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29) -#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10) - -#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24) -#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24) -#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0) - -#define AL_VOCAL_MORPHER_PHONEME_A (0) -#define AL_VOCAL_MORPHER_PHONEME_E (1) -#define AL_VOCAL_MORPHER_PHONEME_I (2) -#define AL_VOCAL_MORPHER_PHONEME_O (3) -#define AL_VOCAL_MORPHER_PHONEME_U (4) -#define AL_VOCAL_MORPHER_PHONEME_AA (5) -#define AL_VOCAL_MORPHER_PHONEME_AE (6) -#define AL_VOCAL_MORPHER_PHONEME_AH (7) -#define AL_VOCAL_MORPHER_PHONEME_AO (8) -#define AL_VOCAL_MORPHER_PHONEME_EH (9) -#define AL_VOCAL_MORPHER_PHONEME_ER (10) -#define AL_VOCAL_MORPHER_PHONEME_IH (11) -#define AL_VOCAL_MORPHER_PHONEME_IY (12) -#define AL_VOCAL_MORPHER_PHONEME_UH (13) -#define AL_VOCAL_MORPHER_PHONEME_UW (14) -#define AL_VOCAL_MORPHER_PHONEME_B (15) -#define AL_VOCAL_MORPHER_PHONEME_D (16) -#define AL_VOCAL_MORPHER_PHONEME_F (17) -#define AL_VOCAL_MORPHER_PHONEME_G (18) -#define AL_VOCAL_MORPHER_PHONEME_J (19) -#define AL_VOCAL_MORPHER_PHONEME_K (20) -#define AL_VOCAL_MORPHER_PHONEME_L (21) -#define AL_VOCAL_MORPHER_PHONEME_M (22) -#define AL_VOCAL_MORPHER_PHONEME_N (23) -#define AL_VOCAL_MORPHER_PHONEME_P (24) -#define AL_VOCAL_MORPHER_PHONEME_R (25) -#define AL_VOCAL_MORPHER_PHONEME_S (26) -#define AL_VOCAL_MORPHER_PHONEME_T (27) -#define AL_VOCAL_MORPHER_PHONEME_V (28) -#define AL_VOCAL_MORPHER_PHONEME_Z (29) - -#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0) -#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1) -#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2) - -#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0) -#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2) -#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0) - -#define AL_VOCAL_MORPHER_MIN_RATE (0.0f) -#define AL_VOCAL_MORPHER_MAX_RATE (10.0f) -#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f) - -/* Pitch shifter effect */ -#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12) -#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12) -#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12) - -#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50) -#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50) -#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0) - -/* Ring modulator effect */ -#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f) -#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f) -#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f) - -#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f) -#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f) -#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f) - -#define AL_RING_MODULATOR_SINUSOID (0) -#define AL_RING_MODULATOR_SAWTOOTH (1) -#define AL_RING_MODULATOR_SQUARE (2) - -#define AL_RING_MODULATOR_MIN_WAVEFORM (0) -#define AL_RING_MODULATOR_MAX_WAVEFORM (2) -#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0) - -/* Autowah effect */ -#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f) -#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f) -#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f) - -#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f) -#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f) -#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f) - -#define AL_AUTOWAH_MIN_RESONANCE (2.0f) -#define AL_AUTOWAH_MAX_RESONANCE (1000.0f) -#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f) - -#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f) -#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f) -#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f) - -/* Compressor effect */ -#define AL_COMPRESSOR_MIN_ONOFF (0) -#define AL_COMPRESSOR_MAX_ONOFF (1) -#define AL_COMPRESSOR_DEFAULT_ONOFF (1) - -/* Equalizer effect */ -#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f) -#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f) -#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f) - -#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f) -#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f) -#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f) - -#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f) -#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f) -#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f) - -#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f) -#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f) -#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f) - -#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f) -#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f) -#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f) - -#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f) -#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f) -#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f) - -#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f) -#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f) -#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f) - -#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f) -#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f) -#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f) - -#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f) -#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f) -#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f) - -#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f) -#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f) -#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f) - - -/* Source parameter value ranges and defaults. */ -#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f) -#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f) -#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f) - -#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f) -#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f) -#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) - -#define AL_MIN_CONE_OUTER_GAINHF (0.0f) -#define AL_MAX_CONE_OUTER_GAINHF (1.0f) -#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f) - -#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE -#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE -#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE - -#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE -#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE -#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE - -#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE -#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE -#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE - - -/* Listener parameter value ranges and defaults. */ -#define AL_MIN_METERS_PER_UNIT FLT_MIN -#define AL_MAX_METERS_PER_UNIT FLT_MAX -#define AL_DEFAULT_METERS_PER_UNIT (1.0f) - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* AL_EFX_H */ diff --git a/release/include/raylib.h b/release/include/raylib.h deleted file mode 100644 index a8e399da1..000000000 --- a/release/include/raylib.h +++ /dev/null @@ -1,1209 +0,0 @@ -/********************************************************************************************** -* -* raylib - A simple and easy-to-use library to learn videogames programming (www.raylib.com) -* -* FEATURES: -* - NO external dependencies, all required libraries included with raylib -* - Multiple platforms support: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, MacOS, UWP, Android, Raspberry Pi, HTML5. -* - Written in plain C code (C99) in PascalCase/camelCase notation -* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES2 - choose at compile) -* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Powerful fonts module with Fonts support (XNA fonts, AngelCode fonts, TTF) -* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) -* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! -* - Flexible Materials system, supporting classic maps and PBR maps -* - Shaders support, including Model shaders and Postprocessing shaders -* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) -* - VR stereo rendering with configurable HMD device parameters -* - Complete bindings to LUA (raylib-lua) and Go (raylib-go) -* -* NOTES: -* One custom font is loaded by default when InitWindow() [core] -* If using OpenGL 3.3 or ES2, one default shader is loaded automatically (internally defined) [rlgl] -* If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads -* -* DEPENDENCIES (included): -* rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) [core] -* glad (github.com/Dav1dde/glad) for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl] -* mini_al (github.com/dr-soft/mini_al) for audio device/context management [audio] -* -* OPTIONAL DEPENDENCIES (included): -* stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) [textures] -* stb_image_resize (Sean Barret) for image resizing algorythms [textures] -* stb_image_write (Sean Barret) for image writting (PNG) [utils] -* stb_truetype (Sean Barret) for ttf fonts loading [text] -* stb_rect_pack (Sean Barret) for rectangles packing [text] -* stb_vorbis (Sean Barret) for OGG audio loading [audio] -* stb_perlin (Sean Barret) for Perlin noise image generation [textures] -* par_shapes (Philip Rideout) for parametric 3d shapes generation [models] -* jar_xm (Joshua Reisenauer) for XM audio module loading [audio] -* jar_mod (Joshua Reisenauer) for MOD audio module loading [audio] -* dr_flac (David Reid) for FLAC audio file loading [audio] -* dr_mp3 (David Reid) for MP3 audio file loading [audio] -* rgif (Charlie Tangora, Ramon Santamaria) for GIF recording [core] -* -* -* LICENSE: zlib/libpng -* -* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, -* BSD-like license that allows static linking with closed source software: -* -* Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RAYLIB_H -#define RAYLIB_H - -#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) - #define RLAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) -#else - #define RLAPI // We are building or using raylib as a static library (or Linux shared library) -#endif - -//---------------------------------------------------------------------------------- -// Some basic Defines -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846f -#endif - -#define DEG2RAD (PI/180.0f) -#define RAD2DEG (180.0f/PI) - -// raylib Config Flags -#define FLAG_SHOW_LOGO 1 // Set to show raylib logo at startup -#define FLAG_FULLSCREEN_MODE 2 // Set to run program in fullscreen -#define FLAG_WINDOW_RESIZABLE 4 // Set to allow resizable window -#define FLAG_WINDOW_UNDECORATED 8 // Set to disable window decoration (frame and buttons) -#define FLAG_WINDOW_TRANSPARENT 16 // Set to allow transparent window -#define FLAG_MSAA_4X_HINT 32 // Set to try enabling MSAA 4X -#define FLAG_VSYNC_HINT 64 // Set to try enabling V-Sync on GPU - -// Keyboard Function Keys -#define KEY_SPACE 32 -#define KEY_ESCAPE 256 -#define KEY_ENTER 257 -#define KEY_TAB 258 -#define KEY_BACKSPACE 259 -#define KEY_INSERT 260 -#define KEY_DELETE 261 -#define KEY_RIGHT 262 -#define KEY_LEFT 263 -#define KEY_DOWN 264 -#define KEY_UP 265 -#define KEY_PAGE_UP 266 -#define KEY_PAGE_DOWN 267 -#define KEY_HOME 268 -#define KEY_END 269 -#define KEY_CAPS_LOCK 280 -#define KEY_SCROLL_LOCK 281 -#define KEY_NUM_LOCK 282 -#define KEY_PRINT_SCREEN 283 -#define KEY_PAUSE 284 -#define KEY_F1 290 -#define KEY_F2 291 -#define KEY_F3 292 -#define KEY_F4 293 -#define KEY_F5 294 -#define KEY_F6 295 -#define KEY_F7 296 -#define KEY_F8 297 -#define KEY_F9 298 -#define KEY_F10 299 -#define KEY_F11 300 -#define KEY_F12 301 -#define KEY_LEFT_SHIFT 340 -#define KEY_LEFT_CONTROL 341 -#define KEY_LEFT_ALT 342 -#define KEY_RIGHT_SHIFT 344 -#define KEY_RIGHT_CONTROL 345 -#define KEY_RIGHT_ALT 346 -#define KEY_GRAVE 96 -#define KEY_SLASH 47 -#define KEY_BACKSLASH 92 - -// Keyboard Alpha Numeric Keys -#define KEY_ZERO 48 -#define KEY_ONE 49 -#define KEY_TWO 50 -#define KEY_THREE 51 -#define KEY_FOUR 52 -#define KEY_FIVE 53 -#define KEY_SIX 54 -#define KEY_SEVEN 55 -#define KEY_EIGHT 56 -#define KEY_NINE 57 -#define KEY_A 65 -#define KEY_B 66 -#define KEY_C 67 -#define KEY_D 68 -#define KEY_E 69 -#define KEY_F 70 -#define KEY_G 71 -#define KEY_H 72 -#define KEY_I 73 -#define KEY_J 74 -#define KEY_K 75 -#define KEY_L 76 -#define KEY_M 77 -#define KEY_N 78 -#define KEY_O 79 -#define KEY_P 80 -#define KEY_Q 81 -#define KEY_R 82 -#define KEY_S 83 -#define KEY_T 84 -#define KEY_U 85 -#define KEY_V 86 -#define KEY_W 87 -#define KEY_X 88 -#define KEY_Y 89 -#define KEY_Z 90 - -// Android Physical Buttons -#define KEY_BACK 4 -#define KEY_MENU 82 -#define KEY_VOLUME_UP 24 -#define KEY_VOLUME_DOWN 25 - -// Mouse Buttons -#define MOUSE_LEFT_BUTTON 0 -#define MOUSE_RIGHT_BUTTON 1 -#define MOUSE_MIDDLE_BUTTON 2 - -// Touch points registered -#define MAX_TOUCH_POINTS 2 - -// Gamepad Number -#define GAMEPAD_PLAYER1 0 -#define GAMEPAD_PLAYER2 1 -#define GAMEPAD_PLAYER3 2 -#define GAMEPAD_PLAYER4 3 - -// Gamepad Buttons/Axis - -// PS3 USB Controller Buttons -#define GAMEPAD_PS3_BUTTON_TRIANGLE 0 -#define GAMEPAD_PS3_BUTTON_CIRCLE 1 -#define GAMEPAD_PS3_BUTTON_CROSS 2 -#define GAMEPAD_PS3_BUTTON_SQUARE 3 -#define GAMEPAD_PS3_BUTTON_L1 6 -#define GAMEPAD_PS3_BUTTON_R1 7 -#define GAMEPAD_PS3_BUTTON_L2 4 -#define GAMEPAD_PS3_BUTTON_R2 5 -#define GAMEPAD_PS3_BUTTON_START 8 -#define GAMEPAD_PS3_BUTTON_SELECT 9 -#define GAMEPAD_PS3_BUTTON_UP 24 -#define GAMEPAD_PS3_BUTTON_RIGHT 25 -#define GAMEPAD_PS3_BUTTON_DOWN 26 -#define GAMEPAD_PS3_BUTTON_LEFT 27 -#define GAMEPAD_PS3_BUTTON_PS 12 - -// PS3 USB Controller Axis -#define GAMEPAD_PS3_AXIS_LEFT_X 0 -#define GAMEPAD_PS3_AXIS_LEFT_Y 1 -#define GAMEPAD_PS3_AXIS_RIGHT_X 2 -#define GAMEPAD_PS3_AXIS_RIGHT_Y 5 -#define GAMEPAD_PS3_AXIS_L2 3 // [1..-1] (pressure-level) -#define GAMEPAD_PS3_AXIS_R2 4 // [1..-1] (pressure-level) - -// Xbox360 USB Controller Buttons -#define GAMEPAD_XBOX_BUTTON_A 0 -#define GAMEPAD_XBOX_BUTTON_B 1 -#define GAMEPAD_XBOX_BUTTON_X 2 -#define GAMEPAD_XBOX_BUTTON_Y 3 -#define GAMEPAD_XBOX_BUTTON_LB 4 -#define GAMEPAD_XBOX_BUTTON_RB 5 -#define GAMEPAD_XBOX_BUTTON_SELECT 6 -#define GAMEPAD_XBOX_BUTTON_START 7 -#define GAMEPAD_XBOX_BUTTON_UP 10 -#define GAMEPAD_XBOX_BUTTON_RIGHT 11 -#define GAMEPAD_XBOX_BUTTON_DOWN 12 -#define GAMEPAD_XBOX_BUTTON_LEFT 13 -#define GAMEPAD_XBOX_BUTTON_HOME 8 - -// Android Gamepad Controller (SNES CLASSIC) -#define GAMEPAD_ANDROID_DPAD_UP 19 -#define GAMEPAD_ANDROID_DPAD_DOWN 20 -#define GAMEPAD_ANDROID_DPAD_LEFT 21 -#define GAMEPAD_ANDROID_DPAD_RIGHT 22 -#define GAMEPAD_ANDROID_DPAD_CENTER 23 - -#define GAMEPAD_ANDROID_BUTTON_A 96 -#define GAMEPAD_ANDROID_BUTTON_B 97 -#define GAMEPAD_ANDROID_BUTTON_C 98 -#define GAMEPAD_ANDROID_BUTTON_X 99 -#define GAMEPAD_ANDROID_BUTTON_Y 100 -#define GAMEPAD_ANDROID_BUTTON_Z 101 -#define GAMEPAD_ANDROID_BUTTON_L1 102 -#define GAMEPAD_ANDROID_BUTTON_R1 103 -#define GAMEPAD_ANDROID_BUTTON_L2 104 -#define GAMEPAD_ANDROID_BUTTON_R2 105 - -// Xbox360 USB Controller Axis -// NOTE: For Raspberry Pi, axis must be reconfigured -#if defined(PLATFORM_RPI) - #define GAMEPAD_XBOX_AXIS_LEFT_X 0 // [-1..1] (left->right) - #define GAMEPAD_XBOX_AXIS_LEFT_Y 1 // [-1..1] (up->down) - #define GAMEPAD_XBOX_AXIS_RIGHT_X 3 // [-1..1] (left->right) - #define GAMEPAD_XBOX_AXIS_RIGHT_Y 4 // [-1..1] (up->down) - #define GAMEPAD_XBOX_AXIS_LT 2 // [-1..1] (pressure-level) - #define GAMEPAD_XBOX_AXIS_RT 5 // [-1..1] (pressure-level) -#else - #define GAMEPAD_XBOX_AXIS_LEFT_X 0 // [-1..1] (left->right) - #define GAMEPAD_XBOX_AXIS_LEFT_Y 1 // [1..-1] (up->down) - #define GAMEPAD_XBOX_AXIS_RIGHT_X 2 // [-1..1] (left->right) - #define GAMEPAD_XBOX_AXIS_RIGHT_Y 3 // [1..-1] (up->down) - #define GAMEPAD_XBOX_AXIS_LT 4 // [-1..1] (pressure-level) - #define GAMEPAD_XBOX_AXIS_RT 5 // [-1..1] (pressure-level) -#endif - -// NOTE: MSC C++ compiler does not support compound literals (C99 feature) -// Plain structures in C++ (without constructors) can be initialized from { } initializers. -#ifdef __cplusplus - #define CLITERAL -#else - #define CLITERAL (Color) -#endif - -// Some Basic Colors -// NOTE: Custom raylib color palette for amazing visuals on WHITE background -#define LIGHTGRAY CLITERAL{ 200, 200, 200, 255 } // Light Gray -#define GRAY CLITERAL{ 130, 130, 130, 255 } // Gray -#define DARKGRAY CLITERAL{ 80, 80, 80, 255 } // Dark Gray -#define YELLOW CLITERAL{ 253, 249, 0, 255 } // Yellow -#define GOLD CLITERAL{ 255, 203, 0, 255 } // Gold -#define ORANGE CLITERAL{ 255, 161, 0, 255 } // Orange -#define PINK CLITERAL{ 255, 109, 194, 255 } // Pink -#define RED CLITERAL{ 230, 41, 55, 255 } // Red -#define MAROON CLITERAL{ 190, 33, 55, 255 } // Maroon -#define GREEN CLITERAL{ 0, 228, 48, 255 } // Green -#define LIME CLITERAL{ 0, 158, 47, 255 } // Lime -#define DARKGREEN CLITERAL{ 0, 117, 44, 255 } // Dark Green -#define SKYBLUE CLITERAL{ 102, 191, 255, 255 } // Sky Blue -#define BLUE CLITERAL{ 0, 121, 241, 255 } // Blue -#define DARKBLUE CLITERAL{ 0, 82, 172, 255 } // Dark Blue -#define PURPLE CLITERAL{ 200, 122, 255, 255 } // Purple -#define VIOLET CLITERAL{ 135, 60, 190, 255 } // Violet -#define DARKPURPLE CLITERAL{ 112, 31, 126, 255 } // Dark Purple -#define BEIGE CLITERAL{ 211, 176, 131, 255 } // Beige -#define BROWN CLITERAL{ 127, 106, 79, 255 } // Brown -#define DARKBROWN CLITERAL{ 76, 63, 47, 255 } // Dark Brown - -#define WHITE CLITERAL{ 255, 255, 255, 255 } // White -#define BLACK CLITERAL{ 0, 0, 0, 255 } // Black -#define BLANK CLITERAL{ 0, 0, 0, 0 } // Blank (Transparent) -#define MAGENTA CLITERAL{ 255, 0, 255, 255 } // Magenta -#define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo) - -// Shader and material limits -#define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct -#define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct - -//---------------------------------------------------------------------------------- -// Structures Definition -//---------------------------------------------------------------------------------- -#ifndef __cplusplus -// Boolean type - #ifndef bool - typedef enum { false, true } bool; - #endif -#endif - -// Vector2 type -typedef struct Vector2 { - float x; - float y; -} Vector2; - -// Vector3 type -typedef struct Vector3 { - float x; - float y; - float z; -} Vector3; - -// Vector4 type -typedef struct Vector4 { - float x; - float y; - float z; - float w; -} Vector4; - -// Quaternion type, same as Vector4 -typedef Vector4 Quaternion; - -// Matrix type (OpenGL style 4x4 - right handed, column major) -typedef struct Matrix { - float m0, m4, m8, m12; - float m1, m5, m9, m13; - float m2, m6, m10, m14; - float m3, m7, m11, m15; -} Matrix; - -// Color type, RGBA (32bit) -typedef struct Color { - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -} Color; - -// Rectangle type -typedef struct Rectangle { - float x; - float y; - float width; - float height; -} Rectangle; - -// Image type, bpp always RGBA (32bit) -// NOTE: Data stored in CPU memory (RAM) -typedef struct Image { - void *data; // Image raw data - int width; // Image base width - int height; // Image base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (PixelFormat type) -} Image; - -// Texture2D type -// NOTE: Data stored in GPU memory -typedef struct Texture2D { - unsigned int id; // OpenGL texture id - int width; // Texture base width - int height; // Texture base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (PixelFormat type) -} Texture2D; - -// Texture type, same as Texture2D -typedef Texture2D Texture; - -// RenderTexture2D type, for texture rendering -typedef struct RenderTexture2D { - unsigned int id; // OpenGL Framebuffer Object (FBO) id - Texture2D texture; // Color buffer attachment texture - Texture2D depth; // Depth buffer attachment texture -} RenderTexture2D; - -// RenderTexture type, same as RenderTexture2D -typedef RenderTexture2D RenderTexture; - -// Font character info -typedef struct CharInfo { - int value; // Character value (Unicode) - Rectangle rec; // Character rectangle in sprite font - int offsetX; // Character offset X when drawing - int offsetY; // Character offset Y when drawing - int advanceX; // Character advance position X - unsigned char *data; // Character pixel data (grayscale) -} CharInfo; - -// Font type, includes texture and charSet array data -typedef struct Font { - Texture2D texture; // Font texture - int baseSize; // Base size (default chars height) - int charsCount; // Number of characters - CharInfo *chars; // Characters info data -} Font; - -#define SpriteFont Font // SpriteFont type fallback, defaults to Font - -// Camera type, defines a camera position/orientation in 3d space -typedef struct Camera3D { - Vector3 position; // Camera position - Vector3 target; // Camera target it looks-at - Vector3 up; // Camera up vector (rotation over its axis) - float fovy; // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic - int type; // Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC -} Camera3D; - -#define Camera Camera3D // Camera type fallback, defaults to Camera3D - -// Camera2D type, defines a 2d camera -typedef struct Camera2D { - Vector2 offset; // Camera offset (displacement from target) - Vector2 target; // Camera target (rotation and zoom origin) - float rotation; // Camera rotation in degrees - float zoom; // Camera zoom (scaling), should be 1.0f by default -} Camera2D; - -// Bounding box type -typedef struct BoundingBox { - Vector3 min; // Minimum vertex box-corner - Vector3 max; // Maximum vertex box-corner -} BoundingBox; - -// Vertex data definning a mesh -// NOTE: Data stored in CPU memory (and GPU) -typedef struct Mesh { - int vertexCount; // Number of vertices stored in arrays - int triangleCount; // Number of triangles stored (indexed or not) - - // Default vertex data - float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - float *texcoords2; // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) - float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) - unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - unsigned short *indices;// Vertex indices (in case vertex data comes indexed) - - // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id - - // OpenGL identifiers - unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (default vertex data) -} Mesh; - -// Shader type (generic) -typedef struct Shader { - unsigned int id; // Shader program id - int locs[MAX_SHADER_LOCATIONS]; // Shader locations array -} Shader; - -// Material texture map -typedef struct MaterialMap { - Texture2D texture; // Material map texture - Color color; // Material map color - float value; // Material map value -} MaterialMap; - -// Material type (generic) -typedef struct Material { - Shader shader; // Material shader - MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps - float *params; // Material generic parameters (if required) -} Material; - -// Model type -typedef struct Model { - Mesh mesh; // Vertex data buffers (RAM and VRAM) - Matrix transform; // Local transform matrix - Material material; // Shader and textures data -} Model; - -// Ray type (useful for raycast) -typedef struct Ray { - Vector3 position; // Ray position (origin) - Vector3 direction; // Ray direction -} Ray; - -// Raycast hit information -typedef struct RayHitInfo { - bool hit; // Did the ray hit something? - float distance; // Distance to nearest hit - Vector3 position; // Position of nearest hit - Vector3 normal; // Surface normal of hit -} RayHitInfo; - -// Wave type, defines audio wave data -typedef struct Wave { - unsigned int sampleCount; // Number of samples - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo) - void *data; // Buffer data pointer -} Wave; - -// Sound source type -typedef struct Sound { - void *audioBuffer; // Pointer to internal data used by the audio system - - unsigned int source; // Audio source id - unsigned int buffer; // Audio buffer id - int format; // Audio format specifier -} Sound; - -// Music type (file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed -typedef struct MusicData *Music; - -// Audio stream type -// NOTE: Useful to create custom audio streams not bound to a specific file -typedef struct AudioStream { - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo) - - void *audioBuffer; // Pointer to internal data used by the audio system. - - int format; // Audio format specifier - unsigned int source; // Audio source id - unsigned int buffers[2]; // Audio buffers (double buffering) -} AudioStream; - -// Head-Mounted-Display device parameters -typedef struct VrDeviceInfo { - int hResolution; // HMD horizontal resolution in pixels - int vResolution; // HMD vertical resolution in pixels - float hScreenSize; // HMD horizontal size in meters - float vScreenSize; // HMD vertical size in meters - float vScreenCenter; // HMD screen center in meters - float eyeToScreenDistance; // HMD distance between eye and display in meters - float lensSeparationDistance; // HMD lens separation distance in meters - float interpupillaryDistance; // HMD IPD (distance between pupils) in meters - float lensDistortionValues[4]; // HMD lens distortion constant parameters - float chromaAbCorrection[4]; // HMD chromatic aberration correction parameters -} VrDeviceInfo; - -//---------------------------------------------------------------------------------- -// Enumerators Definition -//---------------------------------------------------------------------------------- -// Trace log type -typedef enum { - LOG_INFO = 1, - LOG_WARNING = 2, - LOG_ERROR = 4, - LOG_DEBUG = 8, - LOG_OTHER = 16 -} LogType; - -// Shader location point type -typedef enum { - LOC_VERTEX_POSITION = 0, - LOC_VERTEX_TEXCOORD01, - LOC_VERTEX_TEXCOORD02, - LOC_VERTEX_NORMAL, - LOC_VERTEX_TANGENT, - LOC_VERTEX_COLOR, - LOC_MATRIX_MVP, - LOC_MATRIX_MODEL, - LOC_MATRIX_VIEW, - LOC_MATRIX_PROJECTION, - LOC_VECTOR_VIEW, - LOC_COLOR_DIFFUSE, - LOC_COLOR_SPECULAR, - LOC_COLOR_AMBIENT, - LOC_MAP_ALBEDO, // LOC_MAP_DIFFUSE - LOC_MAP_METALNESS, // LOC_MAP_SPECULAR - LOC_MAP_NORMAL, - LOC_MAP_ROUGHNESS, - LOC_MAP_OCCLUSION, - LOC_MAP_EMISSION, - LOC_MAP_HEIGHT, - LOC_MAP_CUBEMAP, - LOC_MAP_IRRADIANCE, - LOC_MAP_PREFILTER, - LOC_MAP_BRDF -} ShaderLocationIndex; - -#define LOC_MAP_DIFFUSE LOC_MAP_ALBEDO -#define LOC_MAP_SPECULAR LOC_MAP_METALNESS - -// Material map type -typedef enum { - MAP_ALBEDO = 0, // MAP_DIFFUSE - MAP_METALNESS = 1, // MAP_SPECULAR - MAP_NORMAL = 2, - MAP_ROUGHNESS = 3, - MAP_OCCLUSION, - MAP_EMISSION, - MAP_HEIGHT, - MAP_CUBEMAP, // NOTE: Uses GL_TEXTURE_CUBE_MAP - MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP - MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP - MAP_BRDF -} TexmapIndex; - -#define MAP_DIFFUSE MAP_ALBEDO -#define MAP_SPECULAR MAP_METALNESS - -// Pixel formats -// NOTE: Support depends on OpenGL version and platform -typedef enum { - UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - UNCOMPRESSED_R5G6B5, // 16 bpp - UNCOMPRESSED_R8G8B8, // 24 bpp - UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - UNCOMPRESSED_R8G8B8A8, // 32 bpp - UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) - COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) - COMPRESSED_DXT3_RGBA, // 8 bpp - COMPRESSED_DXT5_RGBA, // 8 bpp - COMPRESSED_ETC1_RGB, // 4 bpp - COMPRESSED_ETC2_RGB, // 4 bpp - COMPRESSED_ETC2_EAC_RGBA, // 8 bpp - COMPRESSED_PVRT_RGB, // 4 bpp - COMPRESSED_PVRT_RGBA, // 4 bpp - COMPRESSED_ASTC_4x4_RGBA, // 8 bpp - COMPRESSED_ASTC_8x8_RGBA // 2 bpp -} PixelFormat; - -// Texture parameters: filter mode -// NOTE 1: Filtering considers mipmaps if available in the texture -// NOTE 2: Filter is accordingly set for minification and magnification -typedef enum { - FILTER_POINT = 0, // No filter, just pixel aproximation - FILTER_BILINEAR, // Linear filtering - FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) - FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x - FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x - FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x -} TextureFilterMode; - -// Texture parameters: wrap mode -typedef enum { - WRAP_REPEAT = 0, - WRAP_CLAMP, - WRAP_MIRROR -} TextureWrapMode; - -// Color blending modes (pre-defined) -typedef enum { - BLEND_ALPHA = 0, - BLEND_ADDITIVE, - BLEND_MULTIPLIED -} BlendMode; - -// Gestures type -// NOTE: It could be used as flags to enable only some gestures -typedef enum { - GESTURE_NONE = 0, - GESTURE_TAP = 1, - GESTURE_DOUBLETAP = 2, - GESTURE_HOLD = 4, - GESTURE_DRAG = 8, - GESTURE_SWIPE_RIGHT = 16, - GESTURE_SWIPE_LEFT = 32, - GESTURE_SWIPE_UP = 64, - GESTURE_SWIPE_DOWN = 128, - GESTURE_PINCH_IN = 256, - GESTURE_PINCH_OUT = 512 -} Gestures; - -// Camera system modes -typedef enum { - CAMERA_CUSTOM = 0, - CAMERA_FREE, - CAMERA_ORBITAL, - CAMERA_FIRST_PERSON, - CAMERA_THIRD_PERSON -} CameraMode; - -// Camera projection modes -typedef enum { - CAMERA_PERSPECTIVE = 0, - CAMERA_ORTHOGRAPHIC -} CameraType; - -// Head Mounted Display devices -typedef enum { - HMD_DEFAULT_DEVICE = 0, - HMD_OCULUS_RIFT_DK2, - HMD_OCULUS_RIFT_CV1, - HMD_OCULUS_GO, - HMD_VALVE_HTC_VIVE, - HMD_SONY_PSVR -} VrDeviceType; - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -//------------------------------------------------------------------------------------ -// Global Variables Definition -//------------------------------------------------------------------------------------ -// It's lonely here... - -//------------------------------------------------------------------------------------ -// Window and Graphics Device Functions (Module: core) -//------------------------------------------------------------------------------------ - -// Window-related functions -RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context -RLAPI void CloseWindow(void); // Close window and unload OpenGL context -RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully -RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed -RLAPI bool IsWindowMinimized(void); // Check if window has been minimized (or lost focus) -RLAPI void ToggleFullscreen(void); // Toggle fullscreen mode (only PLATFORM_DESKTOP) -RLAPI void SetWindowIcon(Image image); // Set icon for window (only PLATFORM_DESKTOP) -RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) -RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) -RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) -RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) -RLAPI void SetWindowSize(int width, int height); // Set window dimensions -RLAPI int GetScreenWidth(void); // Get current screen width -RLAPI int GetScreenHeight(void); // Get current screen height - -// Cursor-related functions -RLAPI void ShowCursor(void); // Shows cursor -RLAPI void HideCursor(void); // Hides cursor -RLAPI bool IsCursorHidden(void); // Check if cursor is not visible -RLAPI void EnableCursor(void); // Enables cursor (unlock cursor) -RLAPI void DisableCursor(void); // Disables cursor (lock cursor) - -// Drawing-related functions -RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color) -RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing -RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering) -RLAPI void BeginMode2D(Camera2D camera); // Initialize 2D mode with custom camera (2D) -RLAPI void EndMode2D(void); // Ends 2D mode with custom camera -RLAPI void BeginMode3D(Camera3D camera); // Initializes 3D mode with custom camera (3D) -RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode -RLAPI void BeginTextureMode(RenderTexture2D target); // Initializes render texture for drawing -RLAPI void EndTextureMode(void); // Ends drawing to render texture - -// Screen-space-related functions -RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a ray trace from mouse position -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 -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 -RLAPI double GetTime(void); // Returns elapsed time in seconds since InitWindow() - -// Color-related functions -RLAPI int ColorToInt(Color color); // Returns hexadecimal value for a Color -RLAPI Vector4 ColorNormalize(Color color); // Returns color normalized as float [0..1] -RLAPI Vector3 ColorToHSV(Color color); // Returns HSV values for a Color -RLAPI Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value -RLAPI Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f - -// Misc. functions -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 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) - -// Files management functions -RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension -RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string -RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string -RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string) -RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) -RLAPI bool ChangeDirectory(const char *dir); // Change working directory, returns true if success -RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI char **GetDroppedFiles(int *count); // Get dropped files names -RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer - -// Persistent storage management -RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) -RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position) - -//------------------------------------------------------------------------------------ -// Input Handling Functions (Module: core) -//------------------------------------------------------------------------------------ - -// Input-related functions: keyboard -RLAPI bool IsKeyPressed(int key); // Detect if a key has been pressed once -RLAPI bool IsKeyDown(int key); // Detect if a key is being pressed -RLAPI bool IsKeyReleased(int key); // Detect if a key has been released once -RLAPI bool IsKeyUp(int key); // Detect if a key is NOT being pressed -RLAPI int GetKeyPressed(void); // Get latest key pressed -RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) - -// Input-related functions: gamepads -RLAPI bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available -RLAPI bool IsGamepadName(int gamepad, const char *name); // Check gamepad name (if available) -RLAPI const char *GetGamepadName(int gamepad); // Return gamepad internal name id -RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once -RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed -RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once -RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed -RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed -RLAPI int GetGamepadAxisCount(int gamepad); // Return gamepad axis count for a gamepad -RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis - -// Input-related functions: mouse -RLAPI bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once -RLAPI bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed -RLAPI bool IsMouseButtonReleased(int button); // Detect if a mouse button has been released once -RLAPI bool IsMouseButtonUp(int button); // Detect if a mouse button is NOT being pressed -RLAPI int GetMouseX(void); // Returns mouse position X -RLAPI int GetMouseY(void); // Returns mouse position Y -RLAPI Vector2 GetMousePosition(void); // Returns mouse position XY -RLAPI void SetMousePosition(Vector2 position); // Set mouse position XY -RLAPI void SetMouseScale(float scale); // Set mouse scaling -RLAPI int GetMouseWheelMove(void); // Returns mouse wheel movement Y - -// Input-related functions: touch -RLAPI int GetTouchX(void); // Returns touch position X for touch point 0 (relative to screen size) -RLAPI int GetTouchY(void); // Returns touch position Y for touch point 0 (relative to screen size) -RLAPI Vector2 GetTouchPosition(int index); // Returns touch position XY for a touch point index (relative to screen size) - -//------------------------------------------------------------------------------------ -// Gestures and Touch Handling Functions (Module: gestures) -//------------------------------------------------------------------------------------ -RLAPI void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected -RLAPI int GetGestureDetected(void); // Get latest detected gesture -RLAPI int GetTouchPointsCount(void); // Get touch points count -RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds -RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector -RLAPI float GetGestureDragAngle(void); // Get gesture drag angle -RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta -RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle - -//------------------------------------------------------------------------------------ -// Camera System Functions (Module: camera) -//------------------------------------------------------------------------------------ -RLAPI void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available) -RLAPI void UpdateCamera(Camera *camera); // Update camera position for selected mode - -RLAPI void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera) -RLAPI void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera) -RLAPI void SetCameraSmoothZoomControl(int szKey); // Set camera smooth zoom key to combine with mouse (free camera) -RLAPI void SetCameraMoveControls(int frontKey, int backKey, int rightKey, int leftKey, int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras) - -//------------------------------------------------------------------------------------ -// Basic Shapes Drawing Functions (Module: shapes) -//------------------------------------------------------------------------------------ - -// Basic shapes drawing functions -RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel -RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) -RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line -RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) -RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line defining thickness -RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out -RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle -RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) -RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline -RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) -RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters -RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle -RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle -RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors -RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline -RLAPI void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outline with extended parameters -RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle -RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline -RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) -RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points -RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines - -// Basic shapes collision detection functions -RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles -RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles -RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle -RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision -RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle -RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle -RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle - -//------------------------------------------------------------------------------------ -// Texture Loading and Drawing Functions (Module: textures) -//------------------------------------------------------------------------------------ - -// Image/Texture2D data loading/unloading/saving functions -RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) -RLAPI Image LoadImageEx(Color *pixels, int width, int height); // Load image from Color array data (RGBA - 32bit) -RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters -RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI void ExportImage(const char *fileName, Image image); // Export image as a PNG file -RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) -RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data -RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) -RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) -RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) -RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) -RLAPI Color *GetImageData(Image image); // Get pixel data from image as a Color struct array -RLAPI Vector4 *GetImageDataNormalized(Image image); // Get pixel data from image as Vector4 array (float normalized) -RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) -RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image -RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data - -// Image manipulation functions -RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) -RLAPI void ImageToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) -RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format -RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image -RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color -RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value -RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel -RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle -RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering) -RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) -RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color -RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image -RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) -RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) -RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) -RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image -RLAPI void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color); // Draw rectangle within an image -RLAPI void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination) -RLAPI void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color); // Draw text (custom sprite font) within an image (destination) -RLAPI void ImageFlipVertical(Image *image); // Flip image vertically -RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally -RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg -RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg -RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint -RLAPI void ImageColorInvert(Image *image); // Modify image color: invert -RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale -RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) -RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) -RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color - -// Image generation functions -RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient -RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient -RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient -RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked -RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise -RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise -RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm. Bigger tileSize means bigger cells - -// Texture2D configuration functions -RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture -RLAPI void SetTextureFilter(Texture2D texture, int filterMode); // Set texture scaling filter mode -RLAPI void SetTextureWrap(Texture2D texture, int wrapMode); // Set texture wrapping mode - -// Texture2D drawing functions -RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D -RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 -RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters -RLAPI void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle -RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters - -//------------------------------------------------------------------------------------ -// Font Loading and Text Drawing Functions (Module: text) -//------------------------------------------------------------------------------------ - -// Font loading/unloading functions -RLAPI Font GetFontDefault(void); // Get the default Font -RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters -RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, bool sdf); // Load font data for further use -RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) - -// Text drawing functions -RLAPI void DrawFPS(int posX, int posY); // Shows current FPS -RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters - -// Text misc. functions -RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font -RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font -RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' -RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string -RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font - -//------------------------------------------------------------------------------------ -// Basic 3d Shapes Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Basic geometric 3D shapes drawing functions -RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space -RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space -RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube -RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) -RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires -RLAPI void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color); // Draw cube textured -RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere -RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters -RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires -RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone -RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires -RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ -RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line -RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) -RLAPI void DrawGizmo(Vector3 position); // Draw simple gizmo -//DrawTorus(), DrawTeapot() could be useful? - -//------------------------------------------------------------------------------------ -// Model 3d Loading and Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Model loading/unloading functions -RLAPI Model LoadModel(const char *fileName); // Load model from files (mesh and material) -RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh -RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) - -// Mesh loading/unloading functions -RLAPI Mesh LoadMesh(const char *fileName); // Load mesh from file -RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) -RLAPI void ExportMesh(const char *fileName, Mesh mesh); // Export mesh as an OBJ file - -// Mesh manipulation functions -RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents -RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals - -// Mesh generation functions -RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions) -RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh -RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) -RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap) -RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh -RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh -RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh -RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data -RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data - -// Material loading/unloading functions -RLAPI Material LoadMaterial(const char *fileName); // Load material from file -RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) - -// Model drawing functions -RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) -RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters -RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) -RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters -RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) -RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture -RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec - -// Collision detection functions -RLAPI bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); // Detect collision between two spheres -RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Detect collision between two bounding boxes -RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere); // Detect collision between box and sphere -RLAPI bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius); // Detect collision between ray and sphere -RLAPI bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint); // Detect collision between ray and sphere, returns collision point -RLAPI bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box -RLAPI RayHitInfo GetCollisionRayModel(Ray ray, Model *model); // Get collision info between ray and model -RLAPI RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle -RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight); // Get collision info between ray and ground plane (Y-normal plane) - -//------------------------------------------------------------------------------------ -// Shaders System Functions (Module: rlgl) -// NOTE: This functions are useless when using OpenGL 1.1 -//------------------------------------------------------------------------------------ - -// Shader loading/unloading functions -RLAPI char *LoadText(const char *fileName); // Load chars array from text file -RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations -RLAPI Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations -RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) - -RLAPI Shader GetShaderDefault(void); // Get default shader -RLAPI Texture2D GetTextureDefault(void); // Get default texture - -// Shader configuration functions -RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location -RLAPI void SetShaderValue(Shader shader, int uniformLoc, const float *value, int size); // Set shader uniform value (float) -RLAPI void SetShaderValuei(Shader shader, int uniformLoc, const int *value, int size); // Set shader uniform value (int) -RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) -RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) -RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) -RLAPI Matrix GetMatrixModelview(); // Get internal modelview matrix - -// Texture maps generation (PBR) -// NOTE: Required shaders should be provided -RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture -RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data -RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -RLAPI Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data - -// Shading begin/end functions -RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing -RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) -RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) -RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) - -// VR control functions -RLAPI VrDeviceInfo GetVrDeviceInfo(int vrDeviceType); // Get VR device information for some standard devices -RLAPI void InitVrSimulator(VrDeviceInfo info); // Init VR simulator for selected device parameters -RLAPI void CloseVrSimulator(void); // Close VR simulator for current device -RLAPI bool IsVrSimulatorReady(void); // Detect if VR simulator is ready -RLAPI void SetVrDistortionShader(Shader shader); // Set VR distortion shader for stereoscopic rendering -RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -RLAPI void ToggleVrMode(void); // Enable/Disable VR experience -RLAPI void BeginVrDrawing(void); // Begin VR simulator stereo rendering -RLAPI void EndVrDrawing(void); // End VR simulator stereo rendering - -//------------------------------------------------------------------------------------ -// Audio Loading and Playing Functions (Module: audio) -//------------------------------------------------------------------------------------ - -// Audio device management functions -RLAPI void InitAudioDevice(void); // Initialize audio device and context -RLAPI void CloseAudioDevice(void); // Close the audio device and context -RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully -RLAPI void SetMasterVolume(float volume); // Set master volume (listener) - -// Wave/Sound loading/unloading functions -RLAPI Wave LoadWave(const char *fileName); // Load wave data from file -RLAPI Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data -RLAPI Sound LoadSound(const char *fileName); // Load sound from file -RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data -RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data -RLAPI void UnloadWave(Wave wave); // Unload wave data -RLAPI void UnloadSound(Sound sound); // Unload sound - -// Wave/Sound management functions -RLAPI void PlaySound(Sound sound); // Play a sound -RLAPI void PauseSound(Sound sound); // Pause a sound -RLAPI void ResumeSound(Sound sound); // Resume a paused sound -RLAPI void StopSound(Sound sound); // Stop playing a sound -RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing -RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) -RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format -RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave -RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range -RLAPI float *GetWaveData(Wave wave); // Get samples data from wave as a floats array - -// Music management functions -RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file -RLAPI void UnloadMusicStream(Music music); // Unload music stream -RLAPI void PlayMusicStream(Music music); // Start music playing -RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming -RLAPI void StopMusicStream(Music music); // Stop music playing -RLAPI void PauseMusicStream(Music music); // Pause music playing -RLAPI void ResumeMusicStream(Music music); // Resume playing paused music -RLAPI bool IsMusicPlaying(Music music); // Check if music is playing -RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) -RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -RLAPI void SetMusicLoopCount(Music music, int count); // Set music loop count (loop repeats) -RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) -RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) - -// AudioStream management functions -RLAPI AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Init audio stream (to stream raw audio pcm data) -RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data -RLAPI void CloseAudioStream(AudioStream stream); // Close audio stream and free memory -RLAPI bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill -RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream -RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream -RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream -RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing -RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream -RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) -RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) - -#ifdef __cplusplus -} -#endif - -#endif // RAYLIB_H diff --git a/release/libs/android/arm64-v8a/libraylib.a b/release/libs/android/arm64-v8a/libraylib.a deleted file mode 100644 index 915085f7e..000000000 Binary files a/release/libs/android/arm64-v8a/libraylib.a and /dev/null differ diff --git a/release/libs/android/arm64-v8a/libraylib.so b/release/libs/android/arm64-v8a/libraylib.so deleted file mode 100644 index 12de93157..000000000 Binary files a/release/libs/android/arm64-v8a/libraylib.so and /dev/null differ diff --git a/release/libs/android/armeabi-v7a/libraylib.a b/release/libs/android/armeabi-v7a/libraylib.a deleted file mode 100644 index 85bb289b3..000000000 Binary files a/release/libs/android/armeabi-v7a/libraylib.a and /dev/null differ diff --git a/release/libs/android/armeabi-v7a/libraylib.so b/release/libs/android/armeabi-v7a/libraylib.so deleted file mode 100644 index 080996e7f..000000000 Binary files a/release/libs/android/armeabi-v7a/libraylib.so and /dev/null differ diff --git a/release/libs/html5/libraylib.bc b/release/libs/html5/libraylib.bc deleted file mode 100644 index 411be452b..000000000 Binary files a/release/libs/html5/libraylib.bc and /dev/null differ diff --git a/release/libs/linux/libraylib.a b/release/libs/linux/libraylib.a deleted file mode 100644 index 5a14e1d35..000000000 Binary files a/release/libs/linux/libraylib.a and /dev/null differ diff --git a/release/libs/osx/libraylib.a b/release/libs/osx/libraylib.a deleted file mode 100644 index 8520bf7bf..000000000 Binary files a/release/libs/osx/libraylib.a and /dev/null differ diff --git a/release/libs/osx/libraylib.dylib b/release/libs/osx/libraylib.dylib deleted file mode 100644 index 4a0fc9af9..000000000 Binary files a/release/libs/osx/libraylib.dylib and /dev/null differ diff --git a/release/libs/rpi/libraylib.a b/release/libs/rpi/libraylib.a deleted file mode 100644 index 5a9c92ae7..000000000 Binary files a/release/libs/rpi/libraylib.a and /dev/null differ diff --git a/release/libs/win32/mingw32/libraylib.a b/release/libs/win32/mingw32/libraylib.a deleted file mode 100644 index c27c819e3..000000000 Binary files a/release/libs/win32/mingw32/libraylib.a and /dev/null differ diff --git a/release/libs/win32/mingw32/libraylibdll.a b/release/libs/win32/mingw32/libraylibdll.a deleted file mode 100644 index b4f1cb6b9..000000000 Binary files a/release/libs/win32/mingw32/libraylibdll.a and /dev/null differ diff --git a/release/libs/win32/mingw32/raylib.dll b/release/libs/win32/mingw32/raylib.dll deleted file mode 100644 index 54d086ac8..000000000 Binary files a/release/libs/win32/mingw32/raylib.dll and /dev/null differ diff --git a/release/libs/win32/msvc/raylib.dll b/release/libs/win32/msvc/raylib.dll deleted file mode 100644 index 07ff7e905..000000000 Binary files a/release/libs/win32/msvc/raylib.dll and /dev/null differ diff --git a/release/libs/win32/msvc/raylib.lib b/release/libs/win32/msvc/raylib.lib deleted file mode 100644 index 781bebde7..000000000 Binary files a/release/libs/win32/msvc/raylib.lib and /dev/null differ diff --git a/release/libs/win32/msvc/raylibdll.lib b/release/libs/win32/msvc/raylibdll.lib deleted file mode 100644 index e6b17668b..000000000 Binary files a/release/libs/win32/msvc/raylibdll.lib and /dev/null differ diff --git a/release/libs/win32/tcc/libraylib.a b/release/libs/win32/tcc/libraylib.a deleted file mode 100644 index 1aa76249f..000000000 Binary files a/release/libs/win32/tcc/libraylib.a and /dev/null differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5581cc7ab..1b1d0d06d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,13 +45,8 @@ endif() add_definitions("-DRAYLIB_CMAKE=1") if(USE_AUDIO) - if (NOT USE_OPENAL_BACKEND) - file(GLOB mini_al external/mini_al.c) - MESSAGE(STATUS "Audio Backend: mini_al") - else() - find_package(OpenAL REQUIRED) - MESSAGE(STATUS "Audio Backend: OpenAL") - endif() + file(GLOB mini_al external/mini_al.c) + MESSAGE(STATUS "Audio Backend: mini_al") file(GLOB stb_vorbis external/stb_vorbis.c) set(sources ${raylib_sources} ${mini_al} ${stb_vorbis}) else() @@ -73,6 +68,10 @@ if(${PLATFORM} MATCHES "Desktop") find_library(OPENGL_LIBRARY OpenGL) set(LIBS_PRIVATE ${OPENGL_LIBRARY}) link_libraries("${LIBS_PRIVATE}") + if (NOT CMAKE_SYSTEM STRLESS "Darwin-18.0.0") + add_definitions(-DGL_SILENCE_DEPRECATION) + MESSAGE(AUTHOR_WARNING "OpenGL is deprecated starting with macOS 10.14 (Mojave)!") + endif() elseif(WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) else() @@ -131,7 +130,7 @@ if (${OPENGL_VERSION}) elseif (${OPENGL_VERSION} MATCHES "ES 2.0") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") endif() - if (${SUGGESTED_GRAPHICS} AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") + if ("${SUGGESTED_GRAPHICS}" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail") endif() endif() @@ -146,6 +145,7 @@ include(LibraryPathToLinkerFlags) library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}") if(STATIC) + MESSAGE(STATUS "Building raylib static library") if(${PLATFORM} MATCHES "Web") set(CMAKE_STATIC_LIBRARY_SUFFIX ".bc") endif() @@ -182,6 +182,7 @@ endif(STATIC) if(SHARED) + MESSAGE(STATUS "Building raylib shared library") add_library(raylib SHARED ${sources}) target_compile_definitions(raylib @@ -259,7 +260,7 @@ message(STATUS " GRAPHICS=" ${GRAPHICS}) # Packaging SET(CPACK_PACKAGE_NAME "raylib") -SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to learn videogames programming") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to enjoy videogames programming") SET(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") diff --git a/src/CMakeOptions.txt b/src/CMakeOptions.txt index c8af55a0b..eee3f1a97 100644 --- a/src/CMakeOptions.txt +++ b/src/CMakeOptions.txt @@ -12,11 +12,6 @@ option(SHARED "Build raylib as a dynamic library" OFF) option(STATIC "Build raylib as a static library" ON) option(MACOS_FATLIB "Build fat library for both i386 and x86_64 on macOS" OFF) option(USE_AUDIO "Build raylib with audio module" ON) -if(${PLATFORM} MATCHES "Web") - cmake_dependent_option(USE_OPENAL_BACKEND "Link raylib with openAL instead of mini-al" ON "USE_AUDIO" OFF) -else() - cmake_dependent_option(USE_OPENAL_BACKEND "Link raylib with openAL instead of mini-al" OFF "USE_AUDIO" OFF) -endif() enum_option(USE_EXTERNAL_GLFW "OFF;IF_POSSIBLE;ON" "Link raylib against system GLFW instead of embedded one") if(UNIX AND NOT APPLE) @@ -45,6 +40,7 @@ option(SUPPORT_FONT_TEXTURE "Draw rectangle shapes using font texture white char option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON) # textures.c +option(SUPPORT_IMAGE_EXPORT "Support image exporting to file" ON) option(SUPPORT_IMAGE_GENERATION "Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)" ON) option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON) option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON) @@ -72,8 +68,6 @@ option(SUPPORT_FILEFORMAT_MOD "Support loading MOD for sound" ON) option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF}) # utils.c -option(SUPPORT_SAVE_PNG "Support saving image data in PNG file format" ON) -option(SUPPORT_SAVE_BMP "Support saving image data in BMP file format" ${OFF}) option(SUPPORT_TRACELOG "Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown" ON) option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON) @@ -85,6 +79,14 @@ if(NOT (STATIC OR SHARED)) message(FATAL_ERROR "Nothing to do if both -DSHARED=OFF and -DSTATIC=OFF...") endif() +if (DEFINED BUILD_SHARED_LIBS) + set(SHARED ${BUILD_SHARED_LIBS}) + if (${BUILD_SHARED_LIBS}) + set(STATIC OFF) + else() + set(STATIC ON) + endif() +endif() if(DEFINED SHARED_RAYLIB) set(SHARED ${SHARED_RAYLIB}) message(DEPRECATION "-DSHARED_RAYLIB is deprecated. Please use -DSHARED instead.") diff --git a/src/Makefile b/src/Makefile index 26233c95a..0cfc82c16 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,14 +63,6 @@ RAYLIB_BUILD_MODE ?= RELEASE # NOTE: Some programs like tools could not require audio support INCLUDE_AUDIO_MODULE ?= TRUE -# Use OpenAL Soft backend for audio -USE_OPENAL_BACKEND ?= FALSE - -# OpenAL Soft audio backend forced on HTML5 and OSX (see below) -ifeq ($(PLATFORM),PLATFORM_WEB) - USE_OPENAL_BACKEND = TRUE -endif - # Use external GLFW library instead of rglfw module # TODO: Review usage of examples on Linux. USE_EXTERNAL_GLFW ?= FALSE @@ -154,13 +146,6 @@ endif # RAYLIB_PATH ?= /home/pi/raylib #endif -# Force OpenAL Soft audio backend for OSX platform -# NOTE 1: mini_al library does not support CoreAudio yet -# NOTE 2: Required OpenAL libraries should be available on OSX -ifeq ($(PLATFORM_OS),OSX) - USE_OPENAL_BACKEND = TRUE -endif - ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk @@ -201,7 +186,7 @@ RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/release/libs # Define output directory for compiled library ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/win32/mingw32 + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)\release\libs\win32\mingw32 endif ifeq ($(PLATFORM_OS),LINUX) RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/linux @@ -343,11 +328,6 @@ ifeq ($(RAYLIB_LIBTYPE),SHARED) CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED endif -# Use OpenAL Soft backend instead of mini_al -ifeq ($(USE_OPENAL_BACKEND),TRUE) - CFLAGS += -DUSE_OPENAL_BACKEND -endif - # Use Wayland display on Linux desktop ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS), LINUX) @@ -359,7 +339,7 @@ endif # Define include paths for required headers # NOTE: Several external required libraries (stb and others) -INCLUDE_PATHS = -I. -Iexternal -Iexternal/glfw/include +INCLUDE_PATHS = -I. -Iexternal/glfw/include ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),BSD) @@ -426,9 +406,7 @@ endif ifeq ($(INCLUDE_AUDIO_MODULE),TRUE) OBJS += audio.o OBJS += stb_vorbis.o - ifeq ($(USE_OPENAL_BACKEND),FALSE) - OBJS += mini_al.o - endif + OBJS += mini_al.o endif ifeq ($(PLATFORM),PLATFORM_ANDROID) @@ -460,7 +438,13 @@ ifeq ($(PLATFORM_OS),LINUX) endif # Compile raylib library +# NOTE: Release directory is created if not exist raylib: $(OBJS) +ifeq ($(PLATFORM_OS),WINDOWS) + if not exist $(RAYLIB_RELEASE_PATH) mkdir $(RAYLIB_RELEASE_PATH) +else + mkdir -p $(RAYLIB_RELEASE_PATH) +endif ifeq ($(PLATFORM),PLATFORM_WEB) # Compile raylib for web. emcc -O1 $(OBJS) -o $(RAYLIB_RELEASE_PATH)/libraylib.bc diff --git a/src/audio.c b/src/audio.c index 274134b22..f0362b2d0 100644 --- a/src/audio.c +++ b/src/audio.c @@ -16,9 +16,6 @@ * Define to use the module as standalone library (independently of raylib). * Required types and functions are defined in the same module. * -* #define USE_OPENAL_BACKEND -* Use OpenAL Soft audio backend -* * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG * #define SUPPORT_FILEFORMAT_XM @@ -82,25 +79,9 @@ #include "utils.h" // Required for: fopen() Android mapping #endif -#if !defined(USE_OPENAL_BACKEND) - #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. -#endif - -#include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. - -#if !defined(USE_MINI_AL) || (USE_MINI_AL == 0) - #if defined(__APPLE__) - #include "OpenAL/al.h" // OpenAL basic header - #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) - #else - #include "AL/al.h" // OpenAL basic header - #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) - //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS - #endif - - // OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples - // OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) -#endif +#include "external/mini_al.h" // mini_al audio library + // NOTE: Cannot be implement here because it conflicts with + // Win32 APIs: Rectangle, CloseWindow(), ShowCursor(), PlaySoundA() #include // Required for: malloc(), free() #include // Required for: strcmp(), strncmp() @@ -132,7 +113,7 @@ #include "external/dr_mp3.h" // MP3 loading functions #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) #undef bool #endif @@ -147,15 +128,6 @@ // In case of music-stalls, just increase this number #define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) -// Support uncompressed PCM data in 32-bit float IEEE format -// NOTE: This definition is included in "AL/alext.h", but some OpenAL implementations -// could not provide the extensions header (Android), so its defined here -#if !defined(AL_EXT_float32) - #define AL_EXT_float32 1 - #define AL_FORMAT_MONO_FLOAT32 0x10010 - #define AL_FORMAT_STEREO_FLOAT32 0x10011 -#endif - //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -221,6 +193,9 @@ static Wave LoadOGG(const char *fileName); // Load OGG file #if defined(SUPPORT_FILEFORMAT_FLAC) static Wave LoadFLAC(const char *fileName); // Load FLAC file #endif +#if defined(SUPPORT_FILEFORMAT_MP3) +static Wave LoadMP3(const char *fileName); // Load MP3 file +#endif #if defined(AUDIO_STANDALONE) bool IsFileExtension(const char *fileName, const char *ext); // Check file extension @@ -230,8 +205,6 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo //---------------------------------------------------------------------------------- // mini_al AudioBuffer Functionality //---------------------------------------------------------------------------------- -#if USE_MINI_AL - #define DEVICE_FORMAT mal_format_f32 #define DEVICE_CHANNELS 2 #define DEVICE_SAMPLE_RATE 44100 @@ -458,7 +431,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi mal_uint32 totalFramesRemaining = (frameCount - framesRead); if (totalFramesRemaining > 0) { - memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); + memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); // For static buffers we can fill the remaining frames with silence for safety, but we don't want // to report those frames as "read". The reason for this is that the caller uses the return value @@ -484,7 +457,6 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 f } } } -#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Device initialization and Closing @@ -492,7 +464,6 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 f // Initialize audio device void InitAudioDevice(void) { -#if USE_MINI_AL // Context. mal_context_config contextConfig = mal_context_config_init(OnLog); mal_result result = mal_context_init(NULL, 0, &contextConfig, &context); @@ -542,45 +513,11 @@ void InitAudioDevice(void) TraceLog(LOG_INFO, "Audio buffer size: %d", device.bufferSizeInFrames); isAudioInitialized = MAL_TRUE; -#else - // Open and initialize a device with default settings - ALCdevice *device = alcOpenDevice(NULL); - - if (!device) TraceLog(LOG_ERROR, "Audio device could not be opened"); - else - { - ALCcontext *context = alcCreateContext(device, NULL); - - if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE)) - { - if (context != NULL) alcDestroyContext(context); - - alcCloseDevice(device); - - TraceLog(LOG_ERROR, "Could not initialize audio context"); - } - else - { - TraceLog(LOG_INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); - - // Listener definition (just for 2D) - alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); - alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); - alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f); - - alListenerf(AL_GAIN, 1.0f); - - if (alIsExtensionPresent("AL_EXT_float32")) TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 supported"); - else TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 not supported"); - } - } -#endif } // Close the audio device for all contexts void CloseAudioDevice(void) { -#if USE_MINI_AL if (!isAudioInitialized) { TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); @@ -590,18 +527,6 @@ void CloseAudioDevice(void) mal_mutex_uninit(&audioLock); mal_device_uninit(&device); mal_context_uninit(&context); -#else - ALCdevice *device; - ALCcontext *context = alcGetCurrentContext(); - - if (context == NULL) TraceLog(LOG_WARNING, "Could not get current audio context for closing"); - - device = alcGetContextsDevice(context); - - alcMakeContextCurrent(NULL); - alcDestroyContext(context); - alcCloseDevice(device); -#endif TraceLog(LOG_INFO, "Audio device closed successfully"); } @@ -609,20 +534,7 @@ void CloseAudioDevice(void) // Check if device has been initialized successfully bool IsAudioDeviceReady(void) { -#if USE_MINI_AL return isAudioInitialized; -#else - ALCcontext *context = alcGetCurrentContext(); - - if (context == NULL) return false; - else - { - ALCdevice *device = alcGetContextsDevice(context); - - if (device == NULL) return false; - else return true; - } -#endif } // Set master volume (listener) @@ -631,17 +543,13 @@ void SetMasterVolume(float volume) if (volume < 0.0f) volume = 0.0f; else if (volume > 1.0f) volume = 1.0f; -#if USE_MINI_AL masterVolume = volume; -#else - alListenerf(AL_GAIN, volume); -#endif } //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- -#if USE_MINI_AL + // Create a new audio buffer. Initially filled with silence AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) { @@ -840,7 +748,6 @@ void UntrackAudioBuffer(AudioBuffer *audioBuffer) mal_mutex_unlock(&audioLock); } -#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) @@ -857,6 +764,9 @@ Wave LoadWave(const char *fileName) #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName); +#endif +#if defined(SUPPORT_FILEFORMAT_MP3) + else if (IsFileExtension(fileName, ".mp3")) wave = LoadMP3(fileName); #endif else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); @@ -903,7 +813,6 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { -#if USE_MINI_AL // When using mini_al we need to do our own mixing. To simplify this we need convert the format of each sound to be consistent with // the format used to open the playback device. We can do this two ways: // @@ -925,61 +834,6 @@ Sound LoadSoundFromWave(Wave wave) if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed"); sound.audioBuffer = audioBuffer; -#else - ALenum format = 0; - - // The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample) - if (wave.channels == 1) - { - switch (wave.sampleSize) - { - case 8: format = AL_FORMAT_MONO8; break; - case 16: format = AL_FORMAT_MONO16; break; - case 32: format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; - } - } - else if (wave.channels == 2) - { - switch (wave.sampleSize) - { - case 8: format = AL_FORMAT_STEREO8; break; - case 16: format = AL_FORMAT_STEREO16; break; - case 32: format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; - } - } - else TraceLog(LOG_WARNING, "Wave number of channels not supported: %i", wave.channels); - - // Create an audio source - ALuint source; - alGenSources(1, &source); // Generate pointer to audio source - - alSourcef(source, AL_PITCH, 1.0f); - alSourcef(source, AL_GAIN, 1.0f); - alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Convert loaded data to OpenAL buffer - //---------------------------------------- - ALuint buffer; - alGenBuffers(1, &buffer); // Generate pointer to buffer - - unsigned int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; // Size in bytes - - // Upload sound data to buffer - alBufferData(buffer, format, wave.data, dataSize, wave.sampleRate); - - // Attach sound buffer to source - alSourcei(source, AL_BUFFER, buffer); - - TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (%i Hz, %i bit, %s)", source, buffer, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); - - sound.source = source; - sound.buffer = buffer; - sound.format = format; -#endif } return sound; @@ -996,14 +850,7 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { -#if USE_MINI_AL DeleteAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourceStop(sound.source); - - alDeleteSources(1, &sound.source); - alDeleteBuffers(1, &sound.buffer); -#endif TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); } @@ -1012,8 +859,8 @@ void UnloadSound(Sound sound) // NOTE: data must match sound.format void UpdateSound(Sound sound, const void *data, int samplesCount) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer; + if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); @@ -1024,130 +871,136 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) // TODO: May want to lock/unlock this since this data buffer is read at mixing time. memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*mal_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn)); -#else - ALint sampleRate, sampleSize, channels; - alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); - alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format - alGetBufferi(sound.buffer, AL_CHANNELS, &channels); // It could also be retrieved from sound.format +} - TraceLog(LOG_DEBUG, "UpdateSound() : AL_FREQUENCY: %i", sampleRate); - TraceLog(LOG_DEBUG, "UpdateSound() : AL_BITS: %i", sampleSize); - TraceLog(LOG_DEBUG, "UpdateSound() : AL_CHANNELS: %i", channels); +// Export wave data to file +void ExportWave(Wave wave, const char *fileName) +{ + bool success = false; + + if (IsFileExtension(fileName, ".wav")) + { + // Basic WAV headers structs + typedef struct { + char chunkID[4]; + int chunkSize; + char format[4]; + } RiffHeader; - unsigned int dataSize = samplesCount*channels*sampleSize/8; // Size of data in bytes + typedef struct { + char subChunkID[4]; + int subChunkSize; + short audioFormat; + short numChannels; + int sampleRate; + int byteRate; + short blockAlign; + short bitsPerSample; + } WaveFormat; - alSourceStop(sound.source); // Stop sound - alSourcei(sound.source, AL_BUFFER, 0); // Unbind buffer from sound to update - //alDeleteBuffers(1, &sound.buffer); // Delete current buffer data - //alGenBuffers(1, &sound.buffer); // Generate new buffer + typedef struct { + char subChunkID[4]; + int subChunkSize; + } WaveData; + + RiffHeader riffHeader; + WaveFormat waveFormat; + WaveData waveData; - // Upload new data to sound buffer - alBufferData(sound.buffer, sound.format, data, dataSize, sampleRate); + // Fill structs with data + riffHeader.chunkID[0] = 'R'; + riffHeader.chunkID[1] = 'I'; + riffHeader.chunkID[2] = 'F'; + riffHeader.chunkID[3] = 'F'; + riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8; + riffHeader.format[0] = 'W'; + riffHeader.format[1] = 'A'; + riffHeader.format[2] = 'V'; + riffHeader.format[3] = 'E'; - // Attach sound buffer to source again - alSourcei(sound.source, AL_BUFFER, sound.buffer); -#endif + waveFormat.subChunkID[0] = 'f'; + waveFormat.subChunkID[1] = 'm'; + waveFormat.subChunkID[2] = 't'; + waveFormat.subChunkID[3] = ' '; + waveFormat.subChunkSize = 16; + waveFormat.audioFormat = 1; + waveFormat.numChannels = wave.channels; + waveFormat.sampleRate = wave.sampleRate; + waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8; + waveFormat.blockAlign = wave.sampleSize/8; + waveFormat.bitsPerSample = wave.sampleSize; + + waveData.subChunkID[0] = 'd'; + waveData.subChunkID[1] = 'a'; + waveData.subChunkID[2] = 't'; + waveData.subChunkID[3] = 'a'; + waveData.subChunkSize = wave.sampleCount*wave.channels*wave.sampleSize/8; + + FILE *wavFile = fopen(fileName, "wb"); + + if (wavFile == NULL) return; + + fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile); + fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile); + fwrite(&waveData, 1, sizeof(WaveData), wavFile); + + fwrite(wave.data, 1, wave.sampleCount*wave.channels*wave.sampleSize/8, wavFile); + + fclose(wavFile); + + success = true; + } + else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export wave sample data + + if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName); + else TraceLog(LOG_WARNING, "Wave could not be exported."); } // Play a sound void PlaySound(Sound sound) { -#if USE_MINI_AL PlayAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourcePlay(sound.source); // Play the sound -#endif - - //TraceLog(LOG_INFO, "Playing sound"); - - // Find the current position of the sound being played - // NOTE: Only work when the entire file is in a single buffer - //int byteOffset; - //alGetSourcei(sound.source, AL_BYTE_OFFSET, &byteOffset); - // - //int sampleRate; - //alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); // AL_CHANNELS, AL_BITS (bps) - - //float seconds = (float)byteOffset/sampleRate; // Number of seconds since the beginning of the sound - //or - //float result; - //alGetSourcef(sound.source, AL_SEC_OFFSET, &result); // AL_SAMPLE_OFFSET } // Pause a sound void PauseSound(Sound sound) { -#if USE_MINI_AL PauseAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourcePause(sound.source); -#endif } // Resume a paused sound void ResumeSound(Sound sound) { -#if USE_MINI_AL ResumeAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - ALenum state; - - alGetSourcei(sound.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) alSourcePlay(sound.source); -#endif } // Stop reproducing a sound void StopSound(Sound sound) { -#if USE_MINI_AL StopAudioBuffer((AudioBuffer *)sound.audioBuffer); -#else - alSourceStop(sound.source); -#endif } // Check if a sound is playing bool IsSoundPlaying(Sound sound) { -#if USE_MINI_AL return IsAudioBufferPlaying((AudioBuffer *)sound.audioBuffer); -#else - bool playing = false; - ALint state; - - alGetSourcei(sound.source, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) playing = true; - - return playing; -#endif } // Set volume for a sound void SetSoundVolume(Sound sound, float volume) { -#if USE_MINI_AL SetAudioBufferVolume((AudioBuffer *)sound.audioBuffer, volume); -#else - alSourcef(sound.source, AL_GAIN, volume); -#endif } // Set pitch for a sound void SetSoundPitch(Sound sound, float pitch) { -#if USE_MINI_AL SetAudioBufferPitch((AudioBuffer *)sound.audioBuffer, pitch); -#else - alSourcef(sound.source, AL_PITCH, pitch); -#endif } // Convert wave data to desired format void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { -#if USE_MINI_AL mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32)); mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32)); @@ -1175,87 +1028,6 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) wave->channels = channels; free(wave->data); wave->data = data; - -#else - // Format sample rate - // NOTE: Only supported 22050 <--> 44100 - if (wave->sampleRate != sampleRate) - { - // TODO: Resample wave data (upsampling or downsampling) - // NOTE 1: To downsample, you have to drop samples or average them. - // NOTE 2: To upsample, you have to interpolate new samples. - - wave->sampleRate = sampleRate; - } - - // Format sample size - // NOTE: Only supported 8 bit <--> 16 bit <--> 32 bit - if (wave->sampleSize != sampleSize) - { - void *data = malloc(wave->sampleCount*wave->channels*sampleSize/8); - - for (int i = 0; i < wave->sampleCount; i++) - { - for (int j = 0; j < wave->channels; j++) - { - if (sampleSize == 8) - { - if (wave->sampleSize == 16) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float)(((short *)wave->data)[wave->channels*i + j])/32767.0f)*256); - else if (wave->sampleSize == 32) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float *)wave->data)[wave->channels*i + j]*127.0f + 127); - } - else if (sampleSize == 16) - { - if (wave->sampleSize == 8) ((short *)data)[wave->channels*i + j] = (short)(((float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f)*32767); - else if (wave->sampleSize == 32) ((short *)data)[wave->channels*i + j] = (short)((((float *)wave->data)[wave->channels*i + j])*32767); - } - else if (sampleSize == 32) - { - if (wave->sampleSize == 8) ((float *)data)[wave->channels*i + j] = (float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f; - else if (wave->sampleSize == 16) ((float *)data)[wave->channels*i + j] = (float)(((short *)wave->data)[wave->channels*i + j])/32767.0f; - } - } - } - - wave->sampleSize = sampleSize; - free(wave->data); - wave->data = data; - } - - // Format channels (interlaced mode) - // NOTE: Only supported mono <--> stereo - if (wave->channels != channels) - { - void *data = malloc(wave->sampleCount*wave->sampleSize/8*channels); - - if ((wave->channels == 1) && (channels == 2)) // mono ---> stereo (duplicate mono information) - { - for (int i = 0; i < wave->sampleCount; i++) - { - for (int j = 0; j < channels; j++) - { - if (wave->sampleSize == 8) ((unsigned char *)data)[channels*i + j] = ((unsigned char *)wave->data)[i]; - else if (wave->sampleSize == 16) ((short *)data)[channels*i + j] = ((short *)wave->data)[i]; - else if (wave->sampleSize == 32) ((float *)data)[channels*i + j] = ((float *)wave->data)[i]; - } - } - } - else if ((wave->channels == 2) && (channels == 1)) // stereo ---> mono (mix stereo channels) - { - for (int i = 0, j = 0; i < wave->sampleCount; i++, j += 2) - { - if (wave->sampleSize == 8) ((unsigned char *)data)[i] = (((unsigned char *)wave->data)[j] + ((unsigned char *)wave->data)[j + 1])/2; - else if (wave->sampleSize == 16) ((short *)data)[i] = (((short *)wave->data)[j] + ((short *)wave->data)[j + 1])/2; - else if (wave->sampleSize == 32) ((float *)data)[i] = (((float *)wave->data)[j] + ((float *)wave->data)[j + 1])/2.0f; - } - } - - // TODO: Add/remove additional interlaced channels - - wave->channels = channels; - free(wave->data); - wave->data = data; - } -#endif } // Copy a wave to a new wave @@ -1290,7 +1062,7 @@ void WaveCrop(Wave *wave, int initSample, int finalSample) void *data = malloc(sampleCount*wave->sampleSize/8*wave->channels); - memcpy(data, (unsigned char*)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); + memcpy(data, (unsigned char *)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); free(wave->data); wave->data = data; @@ -1374,21 +1146,26 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_MP3) else if (IsFileExtension(fileName, ".mp3")) { - drmp3_init_file(&music->ctxMp3, fileName, NULL); + int result = drmp3_init_file(&music->ctxMp3, fileName, NULL); - if (music->ctxMp3.framesRemaining <= 0) musicLoaded = false; + if (!result) musicLoaded = false; else { - music->stream = InitAudioStream(music->ctxMp3.sampleRate, 16, music->ctxMp3.channels); - music->totalSamples = (unsigned int)music->ctxMp3.framesRemaining*music->ctxMp3.channels; + TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate); + TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32); + TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels); + TraceLog(LOG_INFO, "[%s] MP3 frames remaining: %i", fileName, (unsigned int)music->ctxMp3.framesRemaining); + + music->stream = InitAudioStream(music->ctxMp3.sampleRate, 32, music->ctxMp3.channels); + + // TODO: There is not an easy way to compute the total number of samples available + // in an MP3, frames size could be variable... we tried with a 60 seconds music... but crashes... + music->totalSamples = 60*music->ctxMp3.sampleRate*music->ctxMp3.channels; music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_AUDIO_MP3; music->loopCount = -1; // Infinite loop by default - - TraceLog(LOG_DEBUG, "[%s] MP3 total samples: %i", fileName, music->totalSamples); - TraceLog(LOG_DEBUG, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate); - //TraceLog(LOG_DEBUG, "[%s] MP3 bits per sample: %i", fileName, music->ctxMp3.bitsPerSample); - TraceLog(LOG_DEBUG, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels); + + TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->totalSamples); } } #endif @@ -1485,8 +1262,8 @@ void UnloadMusicStream(Music music) // Start music playing (open stream) void PlayMusicStream(Music music) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer; + if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); @@ -1502,61 +1279,25 @@ void PlayMusicStream(Music music) PlayAudioStream(music->stream); // <-- This resets the cursor position. audioBuffer->frameCursorPos = frameCursorPos; -#else - alSourcePlay(music->stream.source); -#endif } // Pause music playing void PauseMusicStream(Music music) { -#if USE_MINI_AL PauseAudioStream(music->stream); -#else - alSourcePause(music->stream.source); -#endif } // Resume music playing void ResumeMusicStream(Music music) { -#if USE_MINI_AL ResumeAudioStream(music->stream); -#else - ALenum state; - alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) - { - TraceLog(LOG_INFO, "[AUD ID %i] Resume music stream playing", music->stream.source); - alSourcePlay(music->stream.source); - } -#endif } // Stop music playing (close stream) // TODO: To clear a buffer, make sure they have been already processed! void StopMusicStream(Music music) { -#if USE_MINI_AL StopAudioStream(music->stream); -#else - alSourceStop(music->stream.source); - - /* - // Clear stream buffers - // WARNING: Queued buffers must have been processed before unqueueing and reloaded with data!!! - void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); - - for (int i = 0; i < MAX_STREAM_BUFFERS; i++) - { - //UpdateAudioStream(music->stream, pcm, AUDIO_BUFFER_SIZE); // Update one buffer at a time - alBufferData(music->stream.buffers[i], music->stream.format, pcm, AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, music->stream.sampleRate); - } - - free(pcm); - */ -#endif // Restart music context switch (music->ctxType) @@ -1565,6 +1306,9 @@ void StopMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break; #endif +#if defined(SUPPORT_FILEFORMAT_MP3) + case MUSIC_AUDIO_MP3: /* TODO: Restart MP3 context */ break; +#endif #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break; #endif @@ -1581,7 +1325,6 @@ void StopMusicStream(Music music) // TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { -#if USE_MINI_AL bool streamEnding = false; unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2; @@ -1665,132 +1408,24 @@ void UpdateMusicStream(Music music) // just make sure to play again on window restore if (IsMusicPlaying(music)) PlayMusicStream(music); } -#else - ALenum state; - ALint processed = 0; - - alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); // Get music stream state - alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); // Get processed buffers - - if (processed > 0) - { - bool streamEnding = false; - - // NOTE: Using dynamic allocation because it could require more than 16KB - void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); - - int numBuffersToProcess = processed; - int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, - // individual L or R for ogg shorts - - for (int i = 0; i < numBuffersToProcess; i++) - { - if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE; - else samplesCount = music->samplesLeft; - - // TODO: Really don't like ctxType thingy... - switch (music->ctxType) - { - case MUSIC_AUDIO_OGG: - { - // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) - int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*music->stream.channels); - - } break; - #if defined(SUPPORT_FILEFORMAT_FLAC) - case MUSIC_AUDIO_FLAC: - { - // NOTE: Returns the number of samples to process - unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm); - - } break; - #endif - #if defined(SUPPORT_FILEFORMAT_XM) - case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; - #endif - #if defined(SUPPORT_FILEFORMAT_MOD) - case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; - #endif - default: break; - } - - UpdateAudioStream(music->stream, pcm, samplesCount); - music->samplesLeft -= samplesCount; - - if (music->samplesLeft <= 0) - { - streamEnding = true; - break; - } - } - - // Free allocated pcm data - free(pcm); - - // Reset audio stream for looping - if (streamEnding) - { - StopMusicStream(music); // Stop music (and reset) - - // Decrease loopCount to stop when required - if (music->loopCount > 0) - { - music->loopCount--; // Decrease loop count - PlayMusicStream(music); // Play again - } - else - { - if (music->loopCount == -1) - { - PlayMusicStream(music); - } - } - } - else - { - // NOTE: In case window is minimized, music stream is stopped, - // just make sure to play again on window restore - if (state != AL_PLAYING) PlayMusicStream(music); - } - } -#endif } // Check if any music is playing bool IsMusicPlaying(Music music) { -#if USE_MINI_AL return IsAudioStreamPlaying(music->stream); -#else - bool playing = false; - ALint state; - - alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PLAYING) playing = true; - - return playing; -#endif } // Set volume for music void SetMusicVolume(Music music, float volume) { -#if USE_MINI_AL SetAudioStreamVolume(music->stream, volume); -#else - alSourcef(music->stream.source, AL_GAIN, volume); -#endif } // Set pitch for music void SetMusicPitch(Music music, float pitch) { -#if USE_MINI_AL SetAudioStreamPitch(music->stream, pitch); -#else - alSourcef(music->stream.source, AL_PITCH, pitch); -#endif } // Set music loop count (loop repeats) @@ -1836,12 +1471,10 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.channels = 1; // Fallback to mono channel } - -#if USE_MINI_AL mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); // The size of a streaming buffer must be at least double the size of a period. - unsigned int periodSize = device.bufferSizeInFrames / device.periods; + unsigned int periodSize = device.bufferSizeInFrames/device.periods; unsigned int subBufferSize = AUDIO_BUFFER_SIZE; if (subBufferSize < periodSize) subBufferSize = periodSize; @@ -1852,54 +1485,8 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un return stream; } - audioBuffer->looping = true; // Always loop for streaming buffers. + audioBuffer->looping = true; // Always loop for streaming buffers. stream.audioBuffer = audioBuffer; -#else - // Setup OpenAL format - if (stream.channels == 1) - { - switch (sampleSize) - { - case 8: stream.format = AL_FORMAT_MONO8; break; - case 16: stream.format = AL_FORMAT_MONO16; break; - case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; - } - } - else if (stream.channels == 2) - { - switch (sampleSize) - { - case 8: stream.format = AL_FORMAT_STEREO8; break; - case 16: stream.format = AL_FORMAT_STEREO16; break; - case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; - } - } - - // Create an audio source - alGenSources(1, &stream.source); - alSourcef(stream.source, AL_PITCH, 1.0f); - alSourcef(stream.source, AL_GAIN, 1.0f); - alSource3f(stream.source, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(stream.source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - // Create Buffers (double buffering) - alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers); - - // Initialize buffer with zeros by default - // NOTE: Using dynamic allocation because it requires more than 16KB - void *pcm = calloc(AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, 1); - - for (int i = 0; i < MAX_STREAM_BUFFERS; i++) - { - alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, stream.sampleRate); - } - - free(pcm); - - alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); -#endif TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo"); @@ -1909,28 +1496,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // Close audio stream and free memory void CloseAudioStream(AudioStream stream) { -#if USE_MINI_AL DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - // Stop playing channel - alSourceStop(stream.source); - - // Flush out all queued buffers - int queued = 0; - alGetSourcei(stream.source, AL_BUFFERS_QUEUED, &queued); - - ALuint buffer = 0; - - while (queued > 0) - { - alSourceUnqueueBuffers(stream.source, 1, &buffer); - queued--; - } - - // Delete source and buffers - alDeleteSources(1, &stream.source); - alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers); -#endif TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); } @@ -1940,7 +1506,6 @@ void CloseAudioStream(AudioStream stream) // NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed() void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer; if (audioBuffer == NULL) { @@ -1951,6 +1516,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) { mal_uint32 subBufferToUpdate; + if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) { // Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front. @@ -1970,6 +1536,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) if (subBufferSizeInFrames >= (mal_uint32)samplesCount) { mal_uint32 framesToWrite = subBufferSizeInFrames; + if (framesToWrite > (mal_uint32)samplesCount) framesToWrite = (mal_uint32)samplesCount; mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); @@ -1977,6 +1544,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) // Any leftover frames should be filled with zeros. mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; + if (leftoverFrameCount > 0) { memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); @@ -1995,24 +1563,11 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) TraceLog(LOG_ERROR, "Audio buffer not available for updating"); return; } -#else - ALuint buffer = 0; - alSourceUnqueueBuffers(stream.source, 1, &buffer); - - // Check if any buffer was available for unqueue - if (alGetError() != AL_INVALID_VALUE) - { - alBufferData(buffer, stream.format, data, samplesCount*stream.sampleSize/8*stream.channels, stream.sampleRate); - alSourceQueueBuffers(stream.source, 1, &buffer); - } - else TraceLog(LOG_WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source); -#endif } // Check if any audio stream buffers requires refill bool IsAudioBufferProcessed(AudioStream stream) { -#if USE_MINI_AL AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer; if (audioBuffer == NULL) { @@ -2021,92 +1576,46 @@ bool IsAudioBufferProcessed(AudioStream stream) } return audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]; -#else - ALint processed = 0; - - // Determine if music stream is ready to be written - alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed); - - return (processed > 0); -#endif } // Play audio stream void PlayAudioStream(AudioStream stream) { -#if USE_MINI_AL PlayAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - alSourcePlay(stream.source); -#endif } // Play audio stream void PauseAudioStream(AudioStream stream) { -#if USE_MINI_AL PauseAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - alSourcePause(stream.source); -#endif } // Resume audio stream playing void ResumeAudioStream(AudioStream stream) { -#if USE_MINI_AL ResumeAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - ALenum state; - alGetSourcei(stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) alSourcePlay(stream.source); -#endif } // Check if audio stream is playing. bool IsAudioStreamPlaying(AudioStream stream) { -#if USE_MINI_AL return IsAudioBufferPlaying((AudioBuffer *)stream.audioBuffer); -#else - bool playing = false; - ALint state; - - alGetSourcei(stream.source, AL_SOURCE_STATE, &state); - - if (state == AL_PLAYING) playing = true; - - return playing; -#endif } // Stop audio stream void StopAudioStream(AudioStream stream) { -#if USE_MINI_AL StopAudioBuffer((AudioBuffer *)stream.audioBuffer); -#else - alSourceStop(stream.source); -#endif } void SetAudioStreamVolume(AudioStream stream, float volume) { -#if USE_MINI_AL SetAudioBufferVolume((AudioBuffer *)stream.audioBuffer, volume); -#else - alSourcef(stream.source, AL_GAIN, volume); -#endif } void SetAudioStreamPitch(AudioStream stream, float pitch) { -#if USE_MINI_AL SetAudioBufferPitch((AudioBuffer *)stream.audioBuffer, pitch); -#else - alSourcef(stream.source, AL_PITCH, pitch); -#endif } //---------------------------------------------------------------------------------- @@ -2294,6 +1803,33 @@ static Wave LoadFLAC(const char *fileName) } #endif +#if defined(SUPPORT_FILEFORMAT_MP3) +// Load MP3 file into Wave structure +// NOTE: Using dr_mp3 library +static Wave LoadMP3(const char *fileName) +{ + Wave wave = { 0 }; + + // Decode an entire MP3 file in one go + uint64_t totalSampleCount = 0; + drmp3_config config = { 0 }; + wave.data = drmp3_open_and_decode_file_f32(fileName, &config, &totalSampleCount); + + wave.channels = config.outputChannels; + wave.sampleRate = config.outputSampleRate; + wave.sampleCount = (int)totalSampleCount; + wave.sampleSize = 32; + + // NOTE: Only support up to 2 channels (mono, stereo) + if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels); + + if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] MP3 data could not be loaded", fileName); + else TraceLog(LOG_INFO, "[%s] MP3 file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + + return wave; +} +#endif + // Some required functions for audio standalone module version #if defined(AUDIO_STANDALONE) // Check file extension diff --git a/src/camera.h b/src/camera.h index 6bada6669..2ed35f3bf 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/config.h b/src/config.h index 317df46b0..47cc80688 100644 --- a/src/config.h +++ b/src/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.0" +#define RAYLIB_VERSION "2.1-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) @@ -86,6 +86,8 @@ //#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PVR 1 +// Support image export functionality (.png, .bmp, .tga, .jpg) +#define SUPPORT_IMAGE_EXPORT 1 // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... // If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() #define SUPPORT_IMAGE_MANIPULATION 1 @@ -133,10 +135,6 @@ // Show TraceLog() output messages // NOTE: By default LOG_DEBUG traces not shown #define SUPPORT_TRACELOG 1 -// Support saving image data fileformats -// NOTE: Requires stb_image_write library -#define SUPPORT_SAVE_PNG 1 -//#define SUPPORT_SAVE_BMP 1 #endif //defined(RAYLIB_CMAKE) diff --git a/src/config.h.in b/src/config.h.in index f9d474cdc..b0e624805 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,7 +1,5 @@ /* config.h.in */ -#cmakedefine USE_OPENAL_BACKEND 1 - // core.c /* Camera module is included (camera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital */ #cmakedefine SUPPORT_CAMERA_SYSTEM 1 @@ -41,6 +39,9 @@ #cmakedefine SUPPORT_FILEFORMAT_PKM 1 #cmakedefine SUPPORT_FILEFORMAT_PVR 1 +// Support image export functionality (.png, .bmp, .tga, .jpg) +#define SUPPORT_IMAGE_EXPORT 1 + /* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() */ #cmakedefine SUPPORT_IMAGE_MANIPULATION 1 diff --git a/src/core.c b/src/core.c index a1a1b1f98..cb6c7e416 100644 --- a/src/core.c +++ b/src/core.c @@ -123,35 +123,55 @@ #include // Required for: strrchr(), strcmp() //#include // Macros for reporting and retrieving error conditions through error codes #include // Required for: tolower() [Used in IsFileExtension()] +#include // Required for stat() [Used in GetLastWriteTime()] + +#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && defined(_MSC_VER) + #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] +#else + #include // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] +#endif #if defined(_WIN32) #include // Required for: _getch(), _chdir() #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir() #define CHDIR _chdir + #include // Required for _access() [Used in FileExists()] #else - #include "unistd.h" // Required for: getch(), chdir() (POSIX) + #include "unistd.h" // Required for: getch(), chdir() (POSIX), access() #define GETCWD getcwd #define CHDIR chdir #endif -#if defined(__linux__) || defined(PLATFORM_WEB) - #include // Required for: timespec, nanosleep(), select() - POSIX -#elif defined(__APPLE__) - #include // Required for: usleep() -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - #if defined(PLATFORM_WEB) - #define GLFW_INCLUDE_ES2 - #endif - //#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 +#if defined(PLATFORM_DESKTOP) + #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) #include // GLFW3 library: Windows, OpenGL context and Input management // NOTE: GLFW3 already includes gl.h (OpenGL) headers - #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32) - // NOTE: Those functions require linking with winmm library - unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); - unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); + // Support retrieving native window handlers + #if defined(_WIN32) + #define GLFW_EXPOSE_NATIVE_WIN32 + #include // WARNING: It requires customization to avoid windows.h inclusion! + + #if !defined(SUPPORT_BUSY_WAIT_LOOP) + // NOTE: Those functions require linking with winmm library + unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); + unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); + #endif + #elif defined(__linux__) + #include // Required for: timespec, nanosleep(), select() - POSIX + + //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type + //#define GLFW_EXPOSE_NATIVE_WAYLAND + //#define GLFW_EXPOSE_NATIVE_MIR + #include // Required for: glfwGetX11Window() + #elif defined(__APPLE__) + #include // Required for: usleep() + #include // Required for: objc_msgsend(), sel_registerName() + + //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition + #define GLFW_EXPOSE_NATIVE_NSGL + #include // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext() #endif #endif @@ -169,6 +189,7 @@ #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() #include // POSIX threads management (mouse input) + #include // POSIX directory browsing #include // UNIX System call for device-specific input/output operations - ioctl() #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition @@ -189,8 +210,12 @@ #endif #if defined(PLATFORM_WEB) - #include - #include + #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) + #include // GLFW3 library: Windows, OpenGL context and Input management + #include // Required for: timespec, nanosleep(), select() - POSIX + + #include // Emscripten library - LLVM to JavaScript compiler + #include // Emscripten HTML5 library #endif //---------------------------------------------------------------------------------- @@ -199,9 +224,8 @@ #if defined(PLATFORM_RPI) // Old device inputs system #define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input - #define DEFAULT_MOUSE_DEV "/dev/input/mouse0" // Mouse input - #define DEFAULT_TOUCH_DEV "/dev/input/event4" // Touch input virtual device (created by ts_uinput) #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) + #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events // New device input events (evdev) (must be detected) //#define DEFAULT_KEYBOARD_DEV "/dev/input/eventN" @@ -225,14 +249,46 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- + +// Window/Graphics related variables +//----------------------------------------------------------------------------------- #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static GLFWwindow *window; // Native window (graphic device) #endif - static bool windowReady = false; // Check if window has been initialized successfully static bool windowMinimized = false; // Check if window has been minimized static const char *windowTitle = NULL; // Window text title... +#if defined(__APPLE__) +static int windowNeedsUpdating = 2; // Times the Cocoa window needs to be updated initially +#endif + +static unsigned int displayWidth, displayHeight;// Display width and height (monitor, device-screen, LCD, ...) +static int screenWidth, screenHeight; // Screen width and height (used render area) +static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) +static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) +static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) +static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) +static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) + +#if defined(PLATFORM_RPI) +static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) +#endif + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) +static EGLDisplay display; // Native display device (physical screen connection) +static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) +static EGLContext context; // Graphic context, mode in which drawing can be done +static EGLConfig config; // Graphic config +static uint64_t baseTime; // Base time measure for hi-res timer +static bool windowShouldClose = false; // Flag to set window for closing +#endif + +#if defined(PLATFORM_UWP) +extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App) +#endif +//----------------------------------------------------------------------------------- + #if defined(PLATFORM_ANDROID) static struct android_app *androidApp; // Android activity static struct android_poll_source *source; // Android events polling source @@ -243,104 +299,97 @@ static bool appEnabled = true; // Used to detec if app is activ static bool contextRebindRequired = false; // Used to know context rebind required #endif -#if defined(PLATFORM_RPI) -static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) +// Inputs related variables +//----------------------------------------------------------------------------------- +// Keyboard states +static char previousKeyState[512] = { 0 }; // Registers previous frame key state +static char currentKeyState[512] = { 0 }; // Registers current frame key state +static int lastKeyPressed = -1; // Register last key pressed +static int exitKey = KEY_ESCAPE; // Default exit key (ESC) -// Keyboard input variables +#if defined(PLATFORM_RPI) // NOTE: For keyboard we will use the standard input (but reconfigured...) static struct termios defaultKeyboardSettings; // Used to store default keyboard settings static int defaultKeyboardMode; // Used to store default keyboard mode - -// Mouse input variables -static int mouseStream = -1; // Mouse device file descriptor -static bool mouseReady = false; // Flag to know if mouse is ready -static pthread_t mouseThreadId; // Mouse reading thread id - -// Touch input variables -static int touchStream = -1; // Touch device file descriptor -static bool touchReady = false; // Flag to know if touch interface is ready -static pthread_t touchThreadId; // Touch reading thread id - -// Gamepad input variables -static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor -static pthread_t gamepadThreadId; // Gamepad reading thread id -static char gamepadName[64]; // Gamepad name holder #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) -static EGLDisplay display; // Native display device (physical screen connection) -static EGLSurface surface; // Surface to draw on, framebuffers (connected to context) -static EGLContext context; // Graphic context, mode in which drawing can be done -static EGLConfig config; // Graphic config -static uint64_t baseTime; // Base time measure for hi-res timer -static bool windowShouldClose = false; // Flag to set window for closing -#endif - -#if defined(PLATFORM_UWP) -extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App) -#endif - -// Screen related variables -static unsigned int displayWidth, displayHeight; // Display width and height (monitor, device-screen, LCD, ...) -static int screenWidth, screenHeight; // Screen width and height (used render area) -static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) -static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) -static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) -static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) -static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) - -static bool cursorHidden = false; // Track if cursor is hidden -static bool cursorOnScreen = false; // Tracks if cursor is inside client area +// Mouse states +static Vector2 mousePosition; // Mouse position on screen +static float mouseScale = 1.0f; // Mouse default scale +static bool cursorHidden = false; // Track if cursor is hidden +static bool cursorOnScreen = false; // Tracks if cursor is inside client area +static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) -// Register mouse states -static char previousMouseState[3] = { 0 }; // Registers previous mouse button state -static char currentMouseState[3] = { 0 }; // Registers current mouse button state -static int previousMouseWheelY = 0; // Registers previous mouse wheel variation -static int currentMouseWheelY = 0; // Registers current mouse wheel variation +static char previousMouseState[3] = { 0 }; // Registers previous mouse button state +static char currentMouseState[3] = { 0 }; // Registers current mouse button state +static int previousMouseWheelY = 0; // Registers previous mouse wheel variation +static int currentMouseWheelY = 0; // Registers current mouse wheel variation +#endif -// Register gamepads states +#if defined(PLATFORM_RPI) +static int mouseStream = -1; // Mouse device file descriptor +static char currentMouseStateEvdev[3] = { 0 }; // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update) +typedef struct { + pthread_t threadId; // Event reading thread id + int fd; // File descriptor to the device it is assigned to + float sensitivity; // Sensitivitzy multiplier for relative mouse movements + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons +}InputEventWorker; + +static InputEventWorker eventWorkers[10]; // List of worker threads for every monitored "/dev/input/event" + +#endif +#if defined(PLATFORM_WEB) +static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click +#endif + +// Gamepads states +static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed +static int gamepadAxisCount = 0; // Register number of available gamepad axis + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready static float gamepadAxisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state static char previousGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state static char currentGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - -// Keyboard configuration -static int exitKey = KEY_ESCAPE; // Default exit key (ESC) #endif -// Register keyboard states -static char previousKeyState[512] = { 0 }; // Registers previous frame key state -static char currentKeyState[512] = { 0 }; // Registers current frame key state - -static int lastKeyPressed = -1; // Register last key pressed -static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed -static int gamepadAxisCount = 0; // Register number of available gamepad axis - -static Vector2 mousePosition; // Mouse position on screen -static float mouseScale = 1.0f; // Mouse default scale - -#if defined(PLATFORM_WEB) -static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click -#endif - -static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen - -#if defined(PLATFORM_DESKTOP) -static char **dropFilesPath; // Store dropped files paths as strings -static int dropFilesCount = 0; // Count stored strings +#if defined(PLATFORM_RPI) +static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor +static pthread_t gamepadThreadId; // Gamepad reading thread id +static char gamepadName[64]; // Gamepad name holder #endif +//----------------------------------------------------------------------------------- +// Timming system variables +//----------------------------------------------------------------------------------- static double currentTime = 0.0; // Current time measure static double previousTime = 0.0; // Previous time measure static double updateTime = 0.0; // Time measure for frame update static double drawTime = 0.0; // Time measure for frame draw static double frameTime = 0.0; // Time measure for one frame static double targetTime = 0.0; // Desired time for one frame, if 0 not applied +//----------------------------------------------------------------------------------- +// Config internal variables +//----------------------------------------------------------------------------------- static unsigned char configFlags = 0; // Configuration flags (bit based) static bool showLogo = false; // Track if showing logo at init is enabled +#if defined(PLATFORM_DESKTOP) +static char **dropFilesPath; // Store dropped files paths as strings +static int dropFilesCount = 0; // Count dropped files strings +#endif +static char **dirFilesPath; // Store directory files paths as strings +static int dirFilesCount = 0; // Count directory files strings + #if defined(SUPPORT_SCREEN_CAPTURE) static int screenshotCounter = 0; // Screenshots counter #endif @@ -349,6 +398,7 @@ static int screenshotCounter = 0; // Screenshots counter static int gifFramesCounter = 0; // GIF frames counter static bool gifRecording = false; // GIF recording state #endif +//----------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) @@ -362,15 +412,18 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads default fo // Module specific Functions Declaration //---------------------------------------------------------------------------------- static bool InitGraphicsDevice(int width, int height); // Initialize graphics device -static void SetupFramebufferSize(int displayWidth, int displayHeight); +static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void SetupViewport(void); // Set viewport parameters +static void SwapBuffers(void); // Copy back buffer to front buffers + static void InitTimer(void); // Initialize timer static void Wait(float ms); // Wait for some milliseconds (stop program execution) + static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed static void PollInputEvents(void); // Register user events -static void SwapBuffers(void); // Copy back buffer to front buffers + static void LogoAnimation(void); // Plays raylib logo appearing animation -static void SetupViewport(void); // Set viewport parameters #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error @@ -383,7 +436,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored #endif - #if defined(PLATFORM_DESKTOP) static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window #endif @@ -406,9 +458,8 @@ static void InitKeyboard(void); // Init raw keyboard sys static void ProcessKeyboard(void); // Process keyboard events static void RestoreKeyboard(void); // Restore keyboard system static void InitMouse(void); // Mouse initialization (including mouse thread) -static void *MouseThread(void *arg); // Mouse reading thread -static void InitTouch(void); // Touch device initialization (including touch thread) -static void *TouchThread(void *arg); // Touch device reading thread +static void EventThreadSpawn(char* device); // Indetifies a input device and spawns a thread to handle it if needed +static void *EventThread(void *arg); // Input device event reading thread static void InitGamepad(void); // Init raw gamepad input static void *GamepadThread(void *arg); // Mouse reading thread #endif @@ -437,7 +488,7 @@ void android_main(struct android_app *app) androidApp = app; // TODO: Should we maybe report != 0 return codes somewhere? - (void)main(1, (char*[]) { arg0, NULL }); + (void)main(1, (char *[]) { arg0, NULL }); } // TODO: Add this to header (if apps really need it) @@ -525,7 +576,6 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_RPI) // Init raw input system InitMouse(); // Mouse init - InitTouch(); // Touch init InitKeyboard(); // Keyboard init InitGamepad(); // Gamepad init #endif @@ -620,8 +670,13 @@ void CloseWindow(void) windowShouldClose = true; // Added to force threads to exit when the close window is called - pthread_join(mouseThreadId, NULL); - pthread_join(touchThreadId, NULL); + for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) + { + if(eventWorkers[i].threadId == 0) + { + pthread_join(eventWorkers[i].threadId, NULL); + } + } pthread_join(gamepadThreadId, NULL); #endif @@ -770,6 +825,124 @@ int GetScreenHeight(void) return screenHeight; } +// Get native window handle +void *GetWindowHandle(void) +{ +#if defined(_WIN32) + // NOTE: Returned handle is: void *HWND (windows.h) + return glfwGetWin32Window(window); +#elif defined(__linux__) + // NOTE: Returned handle is: unsigned long Window (X.h) + // typedef unsigned long XID; + // typedef XID Window; + //unsigned long id = (unsigned long)glfwGetX11Window(window); + return NULL; // TODO: Find a way to return value... cast to void *? +#elif defined(__APPLE__) + // NOTE: Returned handle is: (objc_object *) + return NULL; // TODO: return (void *)glfwGetCocoaWindow(window); +#else + return NULL; +#endif +} + +// Get number of monitors +int GetMonitorCount(void) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + glfwGetMonitors(&monitorCount); + return monitorCount; +#else + return 1; +#endif +} + +// Get primary monitor width +int GetMonitorWidth(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + return mode->width; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary monitor width +int GetMonitorHeight(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + return mode->height; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary montior physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int physicalWidth; + glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); + return physicalWidth; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get primary monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int physicalHeight; + glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); + return physicalHeight; + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the primary monitor +const char *GetMonitorName(int monitor) +{ +#if defined(PLATFORM_DESKTOP) + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + return glfwGetMonitorName(monitors[monitor]); + } + else TraceLog(LOG_WARNING, "Selected monitor not found"); +#endif + return ""; +} + // Show mouse cursor void ShowCursor() { @@ -886,7 +1059,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; @@ -1297,13 +1470,30 @@ void TakeScreenshot(const char *fileName) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) unsigned char *imgData = rlReadScreenPixels(renderWidth, renderHeight); - SavePNG(fileName, imgData, renderWidth, renderHeight, 4); // Save image as PNG + + Image image = { imgData, renderWidth, renderHeight, 1, UNCOMPRESSED_R8G8B8A8 }; + ExportImage(image, fileName); free(imgData); TraceLog(LOG_INFO, "Screenshot taken: %s", fileName); #endif } +// Check if the file exists +bool FileExists(const char *fileName) +{ + bool result = false; + +#if defined(_WIN32) + if (_access(fileName, 0) != -1) +#else + if (access(fileName, F_OK) != -1) +#endif + result = true; + + return result; +} + // Check file extension bool IsFileExtension(const char *fileName, const char *ext) { @@ -1346,28 +1536,74 @@ const char *GetExtension(const char *fileName) return (dot + 1); } +// String pointer reverse break: returns right-most occurrence of charset in s +static const char *strprbrk(const char *s, const char *charset) +{ + const char *latestMatch = NULL; + for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { } + return latestMatch; +} + // Get pointer to filename for a path string const char *GetFileName(const char *filePath) { - const char *fileName = strrchr(filePath, '\\'); - + const char *fileName = strprbrk(filePath, "\\/"); + if (!fileName || fileName == filePath) return filePath; return fileName + 1; } +// Get filename string without extension (memory should be freed) +const char *GetFileNameWithoutExt(const char *filePath) +{ + char *result, *lastDot, *lastSep; + + char nameDot = '.'; // Default filename to extension separator character + char pathSep = '/'; // Default filepath separator character + + // Error checks and allocate string + if (filePath == NULL) return NULL; + + // Try to allocate new string, same size as original + // NOTE: By default strlen() does not count the '\0' character + if ((result = malloc(strlen(filePath) + 1)) == NULL) return NULL; + + strcpy(result, filePath); // Make a copy of the string + + // NOTE: strrchr() returns a pointer to the last occurrence of character + lastDot = strrchr(result, nameDot); + lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep); + + if (lastDot != NULL) // Check if it has an extension separator... + { + if (lastSep != NULL) // ...and it's before the extenstion separator... + { + if (lastSep < lastDot) + { + *lastDot = '\0'; // ...then remove it + } + } + else *lastDot = '\0'; // Has extension separator with no path separator + } + + return result; // Return the modified string +} // Get directory for a given fileName (with path) const char *GetDirectoryPath(const char *fileName) { - char *lastSlash = NULL; + const char *lastSlash = NULL; static char filePath[256]; // MAX_DIRECTORY_PATH_SIZE = 256 memset(filePath, 0, 256); - - lastSlash = strrchr(fileName, '\\'); + + lastSlash = strprbrk(fileName, "\\/"); + if (!lastSlash) return NULL; + + // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' strncpy(filePath, fileName, strlen(fileName) - (strlen(lastSlash) - 1)); - filePath[strlen(fileName) - strlen(lastSlash)] = '\0'; - + filePath[strlen(fileName) - strlen(lastSlash)] = '\0'; // Add '\0' manually + return filePath; } @@ -1382,6 +1618,57 @@ const char *GetWorkingDirectory(void) return currentDir; } +// Get filenames in a directory path (max 256 files) +// NOTE: Files count is returned by parameters pointer +char **GetDirectoryFiles(const char *dirPath, int *fileCount) +{ + #define MAX_FILEPATH_LENGTH 256 + #define MAX_DIRECTORY_FILES 512 + + ClearDirectoryFiles(); + + // Memory allocation for MAX_DIRECTORY_FILES + dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); + + int counter = 0; + struct dirent *ent; + DIR *dir = opendir(dirPath); + + if (dir != NULL) // It's a directory + { + // TODO: Reading could be done in two passes, + // first one to count files and second one to read names + // That way we can allocate required memory, instead of a limited pool + + while ((ent = readdir(dir)) != NULL) + { + strcpy(dirFilesPath[counter], ent->d_name); + counter++; + } + + closedir(dir); + } + else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file... + + dirFilesCount = counter; + *fileCount = dirFilesCount; + + return dirFilesPath; +} + +// Clear directory files paths buffers +void ClearDirectoryFiles(void) +{ + if (dirFilesCount > 0) + { + for (int i = 0; i < dirFilesCount; i++) free(dirFilesPath[i]); + + free(dirFilesPath); + dirFilesCount = 0; + } +} + // Change working directory, returns true if success bool ChangeDirectory(const char *dir) { @@ -1425,6 +1712,21 @@ void ClearDroppedFiles(void) #endif } +// Get file modification time (last write time) +RLAPI long GetFileModTime(const char *fileName) +{ + struct stat result = { 0 }; + + if (stat(fileName, &result) == 0) + { + time_t mod = result.st_mtime; + + return (long)mod; + } + + return 0; +} + // Save integer value to storage file (to defined position) // NOTE: Storage positions is directly related to file memory layout (4 bytes each integer) void StorageSaveValue(int position, int value) @@ -1696,7 +1998,7 @@ bool IsMouseButtonPressed(int button) #else if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true; #endif - + return pressed; } @@ -1840,7 +2142,9 @@ Vector2 GetTouchPosition(int index) position.x = position.x*((float)renderWidth/(float)displayWidth) - renderOffsetX/2; position.y = position.y*((float)renderHeight/(float)displayHeight) - renderOffsetY/2; } -#else // PLATFORM_DESKTOP, PLATFORM_RPI +#elif defined(PLATFORM_RPI) + position = touchPosition[index]; +#else // PLATFORM_DESKTOP if (index == 0) position = GetMousePosition(); #endif @@ -1981,7 +2285,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function uses and modifies global module variables: // screenWidth/screenHeight - renderWidth/renderHeight - downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); @@ -2215,7 +2519,7 @@ static bool InitGraphicsDevice(int width, int height) } } - //SetupFramebufferSize(displayWidth, displayHeight); + //SetupFramebuffer(displayWidth, displayHeight); EGLint numConfigs = 0; if ((eglChooseConfig(display, framebufferAttribs, &config, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0)) @@ -2321,7 +2625,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); ANativeWindow_setBuffersGeometry(androidApp->window, renderWidth, renderHeight, displayFormat); //ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, displayFormat); // Force use of native display size @@ -2334,7 +2638,7 @@ static bool InitGraphicsDevice(int width, int height) // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView - SetupFramebufferSize(displayWidth, displayHeight); + SetupFramebuffer(displayWidth, displayHeight); dstRect.x = 0; dstRect.y = 0; @@ -2436,7 +2740,7 @@ static void SetupViewport(void) // Compute framebuffer size relative to screen size and display size // NOTE: Global variables renderWidth/renderHeight and renderOffsetX/renderOffsetY can be modified -static void SetupFramebufferSize(int displayWidth, int displayHeight) +static void SetupFramebuffer(int width, int height) { // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var) if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) @@ -2510,7 +2814,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) @@ -2607,6 +2911,21 @@ static void PollInputEvents(void) gamepadAxisCount = 0; #endif +#if defined(PLATFORM_RPI) + // Register previous keys states + for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + + // Register previous mouse states + previousMouseWheelY = currentMouseWheelY; + currentMouseWheelY = 0; + for (int i = 0; i < 3; i++) + { + previousMouseState[i] = currentMouseState[i]; + currentMouseState[i] = currentMouseStateEvdev[i]; + } + +#endif + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // Mouse input polling double mouseX; @@ -2760,6 +3079,16 @@ static void SwapBuffers(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSwapBuffers(window); +#if __APPLE__ + // Workaround for missing/erroneous initial rendering on macOS + if (windowNeedsUpdating) + { + // Desugared version of Objective C: [glfwGetNSGLContext(window) update] + ((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update")); + + windowNeedsUpdating--; + } +#endif #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) @@ -3531,182 +3860,351 @@ static void RestoreKeyboard(void) // Mouse initialization (including mouse thread) static void InitMouse(void) { - // NOTE: We can use /dev/input/mice to read from all available mice - if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0) + char Path[256]; + DIR *d; + struct dirent *dir; + + // Reset variables + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) { - TraceLog(LOG_WARNING, "Mouse device could not be opened, no mouse available"); + touchPosition[i].x = -1; + touchPosition[i].y = -1; } - else + + // Open the linux directory of "/dev/input" + d = opendir(DEFAULT_EVDEV_PATH); + if (d) { - mouseReady = true; - - int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL); - - if (error != 0) TraceLog(LOG_WARNING, "Error creating mouse input event thread"); - else TraceLog(LOG_INFO, "Mouse device initialized successfully"); - } -} - -// Mouse reading thread -// NOTE: We need a separate thread to avoid loosing mouse events, -// if too much time passes between reads, queue gets full and new events override older ones... -static void *MouseThread(void *arg) -{ - const unsigned char XSIGN = (1 << 4); - const unsigned char YSIGN = (1 << 5); - - typedef struct { - char buttons; - char dx, dy; - } MouseEvent; - - MouseEvent mouse; - - int mouseRelX = 0; - int mouseRelY = 0; - - while (!windowShouldClose) - { - if (read(mouseStream, &mouse, sizeof(MouseEvent)) == (int)sizeof(MouseEvent)) + while ((dir = readdir(d)) != NULL) { - if ((mouse.buttons & 0x08) == 0) break; // This bit should always be set - - // Check Left button pressed - if ((mouse.buttons & 0x01) > 0) currentMouseState[0] = 1; - else currentMouseState[0] = 0; - - // Check Right button pressed - if ((mouse.buttons & 0x02) > 0) currentMouseState[1] = 1; - else currentMouseState[1] = 0; - - // Check Middle button pressed - if ((mouse.buttons & 0x04) > 0) currentMouseState[2] = 1; - else currentMouseState[2] = 0; - - mouseRelX = (int)mouse.dx; - mouseRelY = (int)mouse.dy; - - if ((mouse.buttons & XSIGN) > 0) mouseRelX = -1*(255 - mouseRelX); - if ((mouse.buttons & YSIGN) > 0) mouseRelY = -1*(255 - mouseRelY); - - // NOTE: Mouse movement is normalized to not be screen resolution dependant - // We suppose 2*255 (max relative movement) is equivalent to screenWidth (max pixels width) - // Result after normalization is multiplied by MOUSE_SENSITIVITY factor - - mousePosition.x += (float)mouseRelX*((float)screenWidth/(2*255))*MOUSE_SENSITIVITY; - mousePosition.y -= (float)mouseRelY*((float)screenHeight/(2*255))*MOUSE_SENSITIVITY; - - if (mousePosition.x < 0) mousePosition.x = 0; - if (mousePosition.y < 0) mousePosition.y = 0; - - if (mousePosition.x > screenWidth) mousePosition.x = screenWidth; - if (mousePosition.y > screenHeight) mousePosition.y = screenHeight; - } - //else read(mouseStream, &mouse, 1); // Try to sync up again - } - - return NULL; -} - -// Touch initialization (including touch thread) -static void InitTouch(void) -{ - if ((touchStream = open(DEFAULT_TOUCH_DEV, O_RDONLY|O_NONBLOCK)) < 0) - { - TraceLog(LOG_WARNING, "Touch device could not be opened, no touchscreen available"); + if(strncmp("event", dir->d_name, strlen("event")) == 0) // Search for devices named "event*" + { + sprintf(Path, "%s%s", DEFAULT_EVDEV_PATH, dir->d_name); + EventThreadSpawn(Path); // Identify the device and spawn a thread for it + } + } + closedir(d); } else { - touchReady = true; - - int error = pthread_create(&touchThreadId, NULL, &TouchThread, NULL); - - if (error != 0) TraceLog(LOG_WARNING, "Error creating touch input event thread"); - else TraceLog(LOG_INFO, "Touch device initialized successfully"); + TraceLog(LOG_WARNING, "Unable to open linux event directory %s", DEFAULT_EVDEV_PATH); } } -// Touch reading thread. -// This reads from a Virtual Input Event /dev/input/event4 which is -// created by the ts_uinput daemon. This takes, filters and scales -// raw input from the Touchscreen (which appears in /dev/input/event3) -// based on the Calibration data referenced by tslib. -static void *TouchThread(void *arg) +static void EventThreadSpawn(char* device) +{ + #define BITS_PER_LONG (sizeof(long) * 8) + #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) + #define OFF(x) ((x)%BITS_PER_LONG) + #define BIT(x) (1UL<> OFF(bit)) & 1) + struct input_absinfo absinfo; + unsigned long ev_bits[NBITS(EV_MAX)]; + unsigned long abs_bits[NBITS(ABS_MAX)]; + unsigned long rel_bits[NBITS(REL_MAX)]; + unsigned long key_bits[NBITS(KEY_MAX)]; + bool hasAbs = false; + bool hasRel = false; + bool hasAbsMulti = false; + int FreeWorkerId = -1; + int fd = -1; + InputEventWorker* Worker; + + /////////////////////////////////// Open the device and allocate worker ///////////////////////////////////////////// + + // Find a free spot in the workers array + for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) + { + if(eventWorkers[i].threadId == 0) + { + FreeWorkerId = i; + break; + } + } + + // Select the free worker from array + if(FreeWorkerId >= 0) + { + Worker = &(eventWorkers[FreeWorkerId]); // Grab a pointer to the worker + memset(Worker, 0, sizeof(InputEventWorker)); // Clear the worker + } + else + { + TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Out of worker slots", device); + return; + } + + // Open the device + fd = open(device, O_RDONLY | O_NONBLOCK); + if(fd < 0) + { + TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't open device (Err: %d)", device, Worker->fd); + return; + } + Worker->fd = fd; + + // At this point we have a connection to the device, + // but we don't yet know what the device is (Could be + // many things, even as simple as a power button) + + /////////////////////////////////// Identify the device ///////////////////////////////////////////// + + ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); // Read a bitfield of the avalable device properties + + // Check for absolute input devices + if (TEST_BIT(ev_bits, EV_ABS)) + { + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)),abs_bits); + + // Check for absolute movement support (usualy touchscreens, but also joysticks) + if (TEST_BIT(abs_bits, ABS_X) && TEST_BIT(abs_bits, ABS_Y)) + { + hasAbs = true; + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + Worker->absRange.x = absinfo.minimum; + Worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + Worker->absRange.y = absinfo.minimum; + Worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + + // Check for multiple absolute movement support (usualy multitouch touchscreens) + if (TEST_BIT(abs_bits, ABS_MT_POSITION_X) && TEST_BIT(abs_bits, ABS_MT_POSITION_Y)) + { + hasAbsMulti = true; + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + Worker->absRange.x = absinfo.minimum; + Worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + Worker->absRange.y = absinfo.minimum; + Worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + } + + // Check for relative movement support (usualy mouse) + if (TEST_BIT(ev_bits, EV_REL)) + { + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)),rel_bits); + if (TEST_BIT(rel_bits, REL_X) && TEST_BIT(rel_bits, REL_Y)) + { + hasRel = true; + } + } + + // Check for button support to determine the device type(usualy on all input devices) + if (TEST_BIT(ev_bits, EV_KEY)) + { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)),key_bits); + + if(hasAbs || hasAbsMulti) + { + if(TEST_BIT(key_bits, BTN_TOUCH)) + Worker->isTouch = true; // This is a touchscreen + if(TEST_BIT(key_bits, BTN_TOOL_FINGER)) + Worker->isTouch = true; // This is a drawing tablet + if(TEST_BIT(key_bits, BTN_TOOL_PEN)) + Worker->isTouch = true; // This is a drawing tablet + if(TEST_BIT(key_bits, BTN_STYLUS)) + Worker->isTouch = true; // This is a drawing tablet + if(Worker->isTouch || hasAbsMulti) + Worker->isMultitouch = true; // This is a multitouch capable device + } + + if(hasRel) + { + if (TEST_BIT(key_bits, BTN_LEFT)) + Worker->isMouse = true; // This is a mouse + if (TEST_BIT(key_bits, BTN_RIGHT)) + Worker->isMouse = true; // This is a mouse + } + + if (TEST_BIT(key_bits, BTN_A)) + Worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(key_bits, BTN_TRIGGER)) + Worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(key_bits, BTN_START)) + Worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(key_bits, BTN_TL)) + Worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(key_bits, BTN_TL)) + Worker->isGamepad = true; // This is a gamepad + + if (TEST_BIT(key_bits, KEY_SPACE)) + Worker->isKeyboard = true; // This is a keyboard + } + + + /////////////////////////////////// Decide what to do with the device ///////////////////////////////////////////// + if(Worker->isTouch || Worker->isMouse) + { + // Looks like a interesting device + TraceLog(LOG_INFO, "Opening input device '%s' (%s%s%s%s%s)", device, + Worker->isMouse ? "mouse " : "", + Worker->isMultitouch ? "multitouch " : "", + Worker->isTouch ? "touchscreen " : "", + Worker->isGamepad ? "gamepad " : "", + Worker->isKeyboard ? "keyboard " : "" + ); + // Create a thread for this device + int error = pthread_create(&Worker->threadId, NULL, &EventThread, (void*)Worker); + if(error != 0) + { + TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't create thread (Err: %d)", device, error); + Worker->threadId = 0; + close(fd); + } + } + else + { + // We are not interested in this device + close(fd); + } +} + +static void *EventThread(void *arg) { struct input_event ev; GestureEvent gestureEvent; + InputEventWorker* Worker = (InputEventWorker*)arg; + bool GestureNeedsUpdate = false; while (!windowShouldClose) { - if (read(touchStream, &ev, sizeof(ev)) == (int)sizeof(ev)) + if (read(Worker->fd, &ev, sizeof(ev)) == (int)sizeof(ev)) { - // if pressure > 0 then simulate left mouse button click - if (ev.type == EV_ABS && ev.code == 24 && ev.value == 0 && currentMouseState[0] == 1) + /////////////////////////////// Relative movement parsing //////////////////////////////////// + if(ev.type == EV_REL) { - currentMouseState[0] = 0; - gestureEvent.touchAction = TOUCH_UP; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - if (ev.type == EV_ABS && ev.code == 24 && ev.value > 0 && currentMouseState[0] == 0) - { - currentMouseState[0] = 1; - gestureEvent.touchAction = TOUCH_DOWN; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - // x & y values supplied by event4 have been scaled & de-jittered using tslib calibration data - if (ev.type == EV_ABS && ev.code == 0) - { - mousePosition.x = ev.value; - if (mousePosition.x < 0) mousePosition.x = 0; - if (mousePosition.x > screenWidth) mousePosition.x = screenWidth; - gestureEvent.touchAction = TOUCH_MOVE; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); - } - if (ev.type == EV_ABS && ev.code == 1) - { - mousePosition.y = ev.value; - if (mousePosition.y < 0) mousePosition.y = 0; - if (mousePosition.y > screenHeight) mousePosition.y = screenHeight; - gestureEvent.touchAction = TOUCH_MOVE; - gestureEvent.pointCount = 1; - gestureEvent.pointerId[0] = 0; - gestureEvent.pointerId[1] = 1; - gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - gestureEvent.position[1].x /= (float)GetScreenWidth(); - gestureEvent.position[1].y /= (float)GetScreenHeight(); - ProcessGestureEvent(gestureEvent); + if(ev.code == REL_X) + { + mousePosition.x += ev.value; + touchPosition[0].x = mousePosition.x; + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + if(ev.code == REL_Y) + { + mousePosition.y += ev.value; + touchPosition[0].y = mousePosition.y; + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + if(ev.code == REL_WHEEL) + { + currentMouseWheelY += ev.value; + } } + /////////////////////////////// Absolute movement parsing //////////////////////////////////// + if(ev.type == EV_ABS) + { + // Basic movement + if(ev.code == ABS_X) + { + mousePosition.x = (ev.value - Worker->absRange.x) * screenWidth / Worker->absRange.width; //Scale acording to absRange + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + if(ev.code == ABS_Y) + { + mousePosition.y = (ev.value - Worker->absRange.y) * screenHeight / Worker->absRange.height; //Scale acording to absRange + gestureEvent.touchAction = TOUCH_MOVE; + GestureNeedsUpdate = true; + } + + //Multitouch movement + if(ev.code == ABS_MT_SLOT) + { + Worker->touchSlot = ev.value; //Remeber the slot number for the folowing events + } + + if(ev.code == ABS_MT_POSITION_X) + { + if(Worker->touchSlot < MAX_TOUCH_POINTS) + touchPosition[Worker->touchSlot].x = (ev.value - Worker->absRange.x) * screenWidth / Worker->absRange.width; //Scale acording to absRange + } + + if(ev.code == ABS_MT_POSITION_Y) + { + if(Worker->touchSlot < MAX_TOUCH_POINTS) + touchPosition[Worker->touchSlot].y = (ev.value - Worker->absRange.y) * screenHeight / Worker->absRange.height; //Scale acording to absRange + } + + if(ev.code == ABS_MT_TRACKING_ID) + { + if( (ev.value < 0) && (Worker->touchSlot < MAX_TOUCH_POINTS) ) + { + //Touch has ended for this point + touchPosition[Worker->touchSlot].x = -1; + touchPosition[Worker->touchSlot].y = -1; + } + } + } + + /////////////////////////////// Button parsing //////////////////////////////////// + if(ev.type == EV_KEY) + { + if((ev.code == BTN_TOUCH) || (ev.code == BTN_LEFT)) + { + currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = ev.value; + if(ev.value > 0) + gestureEvent.touchAction = TOUCH_DOWN; + else + gestureEvent.touchAction = TOUCH_UP; + GestureNeedsUpdate = true; + } + + if(ev.code == BTN_RIGHT) + { + currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = ev.value; + } + + if(ev.code == BTN_MIDDLE) + { + currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] = ev.value; + } + + } + + /////////////////////////////// Screen confinement //////////////////////////////////// + if(mousePosition.x < 0) + mousePosition.x = 0; + if(mousePosition.x > screenWidth / mouseScale) + mousePosition.x = screenWidth / mouseScale; + + if(mousePosition.y < 0) + mousePosition.y = 0; + if(mousePosition.y > screenHeight / mouseScale) + mousePosition.y = screenHeight / mouseScale; + + /////////////////////////////// Gesture update //////////////////////////////////// + if(GestureNeedsUpdate) + { + gestureEvent.pointCount = 0; + if(touchPosition[0].x >= 0) gestureEvent.pointCount++; + if(touchPosition[1].x >= 0) gestureEvent.pointCount++; + if(touchPosition[2].x >= 0) gestureEvent.pointCount++; + if(touchPosition[3].x >= 0) gestureEvent.pointCount++; + gestureEvent.pointerId[0] = 0; + gestureEvent.pointerId[1] = 1; + gestureEvent.pointerId[2] = 2; + gestureEvent.pointerId[3] = 3; + gestureEvent.position[0] = touchPosition[0]; + gestureEvent.position[1] = touchPosition[1]; + gestureEvent.position[2] = touchPosition[2]; + gestureEvent.position[3] = touchPosition[3]; + ProcessGestureEvent(gestureEvent); + } + } + else + { + usleep(5000); //Sleep for 5ms to avoid hogging CPU time } } return NULL; @@ -3792,6 +4290,10 @@ static void *GamepadThread(void *arg) } } } + else + { + usleep(1000); //Sleep for 1ms to avoid hogging CPU time + } } } diff --git a/src/external/cgltf.h b/src/external/cgltf.h new file mode 100644 index 000000000..ed04d545c --- /dev/null +++ b/src/external/cgltf.h @@ -0,0 +1,2023 @@ +#ifndef CGLTF_H_INCLUDED__ +#define CGLTF_H_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef unsigned long cgltf_size; +typedef float cgltf_float; +typedef int cgltf_bool; + +typedef enum cgltf_file_type +{ + cgltf_file_type_invalid, + cgltf_file_type_gltf, + cgltf_file_type_glb, +} cgltf_file_type; + +typedef struct cgltf_options +{ + cgltf_file_type type; + cgltf_size json_token_count; /* 0 == auto */ + void* (*memory_alloc)(void* user, cgltf_size size); + void (*memory_free) (void* user, void* ptr); + void* memory_user_data; +} cgltf_options; + +typedef enum cgltf_result +{ + cgltf_result_success, + cgltf_result_data_too_short, + cgltf_result_unknown_format, + cgltf_result_invalid_json, + cgltf_result_invalid_options, +} cgltf_result; + +typedef enum cgltf_buffer_view_type +{ + cgltf_buffer_view_type_invalid, + cgltf_buffer_view_type_indices, + cgltf_buffer_view_type_vertices, +} cgltf_buffer_view_type; + +typedef enum cgltf_attribute_type +{ + cgltf_attribute_type_invalid, + cgltf_attribute_type_position, + cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, + cgltf_attribute_type_texcoord_0, + cgltf_attribute_type_texcoord_1, + cgltf_attribute_type_color_0, + cgltf_attribute_type_joints_0, + cgltf_attribute_type_weights_0, +} cgltf_attribute_type; + +typedef enum cgltf_component_type +{ + cgltf_component_type_invalid, + cgltf_component_type_rgb_32f, + cgltf_component_type_rgba_32f, + cgltf_component_type_rg_32f, + cgltf_component_type_rg_8, + cgltf_component_type_rg_16, + cgltf_component_type_rgba_8, + cgltf_component_type_rgba_16, + cgltf_component_type_r_8, + cgltf_component_type_r_8u, + cgltf_component_type_r_16, + cgltf_component_type_r_16u, + cgltf_component_type_r_32u, + cgltf_component_type_r_32f, +} cgltf_component_type; + +typedef enum cgltf_type +{ + cgltf_type_invalid, + cgltf_type_scalar, + cgltf_type_vec2, + cgltf_type_vec3, + cgltf_type_vec4, + cgltf_type_mat2, + cgltf_type_mat3, + cgltf_type_mat4, +} cgltf_type; + +typedef enum cgltf_primitive_type +{ + cgltf_type_points, + cgltf_type_lines, + cgltf_type_line_loop, + cgltf_type_line_strip, + cgltf_type_triangles, + cgltf_type_triangle_strip, + cgltf_type_triangle_fan, +} cgltf_primitive_type; + +typedef struct cgltf_buffer +{ + cgltf_size size; + char* uri; +} cgltf_buffer; + +typedef struct cgltf_buffer_view +{ + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; /* 0 == automatically determined by accessor */ + cgltf_buffer_view_type type; +} cgltf_buffer_view; + +typedef struct cgltf_accessor +{ + cgltf_component_type component_type; + cgltf_type type; + cgltf_size offset; + cgltf_size count; + cgltf_size stride; + cgltf_buffer_view* buffer_view; +} cgltf_accessor; + +typedef struct cgltf_attribute +{ + cgltf_attribute_type name; + cgltf_accessor* data; +} cgltf_attribute; + + +typedef struct cgltf_rgba +{ + cgltf_float r; + cgltf_float g; + cgltf_float b; + cgltf_float a; +} cgltf_rgba; + +typedef struct cgltf_image +{ + char* uri; + cgltf_buffer_view* buffer_view; + char* mime_type; +} cgltf_image; + +typedef struct cgltf_sampler +{ + cgltf_float mag_filter; + cgltf_float min_filter; + cgltf_float wrap_s; + cgltf_float wrap_t; +} cgltf_sampler; + +typedef struct cgltf_texture +{ + cgltf_image* image; + cgltf_sampler* sampler; +} cgltf_texture; + +typedef struct cgltf_texture_view +{ + cgltf_texture* texture; + cgltf_size texcoord; + cgltf_float scale; +} cgltf_texture_view; + +typedef struct cgltf_pbr +{ + cgltf_texture_view base_color_texture; + cgltf_texture_view metallic_roughness_texture; + + cgltf_rgba base_color; + cgltf_float metallic_factor; + cgltf_float roughness_factor; +} cgltf_pbr; + +typedef struct cgltf_material +{ + char* name; + cgltf_pbr pbr; + cgltf_rgba emissive_color; + cgltf_texture_view normal_texture; + cgltf_texture_view emissive_texture; + cgltf_texture_view occlusion_texture; + cgltf_bool double_sided; +} cgltf_material; + +typedef struct cgltf_primitive { + cgltf_primitive_type type; + cgltf_accessor* indices; + cgltf_material* material; + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_primitive; + +typedef struct cgltf_mesh { + char* name; + cgltf_primitive* primitives; + cgltf_size primitives_count; +} cgltf_mesh; + +typedef struct cgltf_data +{ + unsigned version; + cgltf_file_type file_type; + + cgltf_mesh* meshes; + cgltf_size meshes_count; + + cgltf_material* materials; + cgltf_size materials_count; + + cgltf_accessor* accessors; + cgltf_size accessors_count; + + cgltf_buffer_view* buffer_views; + cgltf_size buffer_views_count; + + cgltf_buffer* buffers; + cgltf_size buffers_count; + + cgltf_image* images; + cgltf_size images_count; + + cgltf_texture* textures; + cgltf_size textures_count; + + cgltf_sampler* samplers; + cgltf_size samplers_count; + + const void* bin; + cgltf_size bin_size; + + void (*memory_free) (void* user, void* ptr); + void* memory_user_data; +} cgltf_data; + +cgltf_result cgltf_parse( + const cgltf_options* options, + const void* data, + cgltf_size size, + cgltf_data* out_data); + +void cgltf_free(cgltf_data* data); + +#endif /* #ifndef CGLTF_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#ifdef __INTELLISENSE__ +/* This makes MSVC intellisense work. */ +#define CGLTF_IMPLEMENTATION +#endif + +#ifdef CGLTF_IMPLEMENTATION + +#include /* For uint8_t, uint32_t */ +#include /* For strncpy */ +#include /* For malloc, free */ + + +/* + * -- jsmn.h start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; +void jsmn_init(jsmn_parser *parser); +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens); +/* + * -- jsmn.h end -- + */ + + +static const cgltf_size GltfHeaderSize = 12; +static const cgltf_size GltfChunkHeaderSize = 8; +static const uint32_t GltfMagic = 0x46546C67; +static const uint32_t GltfMagicJsonChunk = 0x4E4F534A; +static const uint32_t GltfMagicBinChunk = 0x004E4942; + +static void* cgltf_mem_alloc(void* user, cgltf_size size) +{ + return malloc(size); +} + +static void cgltf_mem_free(void* user, void* ptr) +{ + free(ptr); +} + +static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data); + +cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data* out_data) +{ + if (size < GltfHeaderSize) + { + return cgltf_result_data_too_short; + } + + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_options fixed_options = *options; + if (fixed_options.memory_alloc == NULL) + { + fixed_options.memory_alloc = &cgltf_mem_alloc; + } + if (fixed_options.memory_free == NULL) + { + fixed_options.memory_free = &cgltf_mem_free; + } + + uint32_t tmp; + // Magic + memcpy(&tmp, data, 4); + if (tmp != GltfMagic) + { + if (fixed_options.type == cgltf_file_type_invalid) + { + fixed_options.type = cgltf_file_type_gltf; + } + else + { + return cgltf_result_unknown_format; + } + } + + memset(out_data, 0, sizeof(cgltf_data)); + out_data->memory_free = fixed_options.memory_free; + out_data->memory_user_data = fixed_options.memory_user_data; + + if (fixed_options.type == cgltf_file_type_gltf) + { + out_data->file_type = cgltf_file_type_gltf; + return cgltf_parse_json(&fixed_options, data, size, out_data); + } + + const uint8_t* ptr = (const uint8_t*)data; + // Version + memcpy(&tmp, ptr + 4, 4); + out_data->version = tmp; + + // Total length + memcpy(&tmp, ptr + 8, 4); + if (tmp > size) + { + return cgltf_result_data_too_short; + } + + const uint8_t* json_chunk = ptr + GltfHeaderSize; + + // JSON chunk: length + uint32_t json_length; + memcpy(&json_length, json_chunk, 4); + if (GltfHeaderSize + GltfChunkHeaderSize + json_length > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: magic + memcpy(&tmp, json_chunk + 4, 4); + if (tmp != GltfMagicJsonChunk) + { + return cgltf_result_unknown_format; + } + + json_chunk += GltfChunkHeaderSize; + cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + out_data->file_type = cgltf_file_type_invalid; + if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize <= size) + { + // We can read another chunk + const uint8_t* bin_chunk = json_chunk + json_length; + + // Bin chunk: length + uint32_t bin_length; + memcpy(&bin_length, bin_chunk, 4); + if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize + bin_length > size) + { + return cgltf_result_data_too_short; + } + + // Bin chunk: magic + memcpy(&tmp, bin_chunk + 4, 4); + if (tmp != GltfMagicBinChunk) + { + return cgltf_result_unknown_format; + } + + bin_chunk += GltfChunkHeaderSize; + + out_data->file_type = cgltf_file_type_glb; + out_data->bin = bin_chunk; + out_data->bin_size = bin_length; + } + + return cgltf_result_success; +} + +void cgltf_free(cgltf_data* data) +{ + data->memory_free(data->memory_user_data, data->accessors); + data->memory_free(data->memory_user_data, data->buffer_views); + + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + data->memory_free(data->memory_user_data, data->buffers[i].uri); + } + data->memory_free(data->memory_user_data, data->buffers); + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + data->memory_free(data->memory_user_data, data->meshes[i].name); + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + data->memory_free(data->memory_user_data, data->meshes[i].primitives[j].attributes); + } + data->memory_free(data->memory_user_data, data->meshes[i].primitives); + } + data->memory_free(data->memory_user_data, data->meshes); + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + data->memory_free(data->memory_user_data, data->materials[i].name); + } + + data->memory_free(data->memory_user_data, data->materials); + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + data->memory_free(data->memory_user_data, data->images[i].uri); + data->memory_free(data->memory_user_data, data->images[i].mime_type); + } + + data->memory_free(data->memory_user_data, data->images); + data->memory_free(data->memory_user_data, data->textures); + data->memory_free(data->memory_user_data, data->samplers); +} + +#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return -128; } + +static char cgltf_to_lower(char c) +{ + if (c >= 'A' && c <= 'Z') + { + c = 'a' + (c - 'A'); + } + return c; +} + +static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); + int const str_len = strlen(str); + int const name_length = tok->end - tok->start; + if (name_length == str_len) + { + for (int i = 0; i < str_len; ++i) + { + char const a = cgltf_to_lower(*((const char*)json_chunk + tok->start + i)); + char const b = cgltf_to_lower(*(str + i)); + if (a < b) + { + return -1; + } + else if (a > b) + { + return 1; + } + } + return 0; + } + return 128; +} + +static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + char tmp[128]; + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + int size = tok->end - tok->start; + strncpy(tmp, + (const char*)json_chunk + tok->start, + size); + tmp[size] = 0; + return atoi(tmp); +} + +static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) { + char tmp[128]; + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + int size = tok->end - tok->start; + strncpy(tmp, + (const char*)json_chunk + tok->start, + size); + tmp[size] = 0; + return atof(tmp); +} + +static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) { + //TODO: error handling? + if (memcmp(json_chunk + tok->start, "true", 4) == 0) + return 1; + + return 0; +} + +static int cgltf_skip_json(jsmntok_t const* tokens, int i) +{ + if (tokens[i].type == JSMN_ARRAY) + { + int size = tokens[i].size; + ++i; + for (int j = 0; j < size; ++j) + { + i = cgltf_skip_json(tokens, i); + } + } + else if (tokens[i].type == JSMN_OBJECT) + { + int size = tokens[i].size; + ++i; + for (int j = 0; j < size; ++j) + { + i = cgltf_skip_json(tokens, i); + i = cgltf_skip_json(tokens, i); + } + } + else if (tokens[i].type == JSMN_PRIMITIVE + || tokens[i].type == JSMN_STRING) + { + return i + 1; + } + return i; +} + + +static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, + cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + out_prim->indices = (void* )-1; + out_prim->material = NULL; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + out_prim->type + = (cgltf_primitive_type) + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + out_prim->indices = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) + { + ++i; + out_prim->material = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) + { + ++i; + if (tokens[i].type != JSMN_OBJECT) + { + return -1; + } + out_prim->attributes_count = tokens[i].size; + out_prim->attributes + = options->memory_alloc(options->memory_user_data, sizeof(cgltf_attribute) * tokens[i].size); + ++i; + for (cgltf_size iattr = 0; iattr < out_prim->attributes_count; ++iattr) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + out_prim->attributes[iattr].name = cgltf_attribute_type_invalid; + if (cgltf_json_strcmp(tokens+i, json_chunk, "POSITION") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_position; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "NORMAL") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_normal; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TANGENT") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_tangent; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_0; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_1") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_1; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_color_0; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "JOINTS_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_joints_0; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "WEIGHTS_0") == 0) + { + out_prim->attributes[iattr].name = cgltf_attribute_type_weights_0; + } + ++i; + out_prim->attributes[iattr].data = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size mesh_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_data->meshes[mesh_index].name = NULL; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->meshes[mesh_index].name = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->meshes[mesh_index].name, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->meshes[mesh_index].name[strsize] = 0; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) + { + ++i; + if (tokens[i].type != JSMN_ARRAY) + { + return -1; + } + out_data->meshes[mesh_index].primitives_count = tokens[i].size; + out_data->meshes[mesh_index].primitives = options->memory_alloc(options->memory_user_data, sizeof(cgltf_primitive) * tokens[i].size); + ++i; + + for (cgltf_size prim_index = 0; + prim_index < out_data->meshes[mesh_index].primitives_count; + ++prim_index) + { + i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, + &out_data->meshes[mesh_index].primitives[prim_index]); + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->meshes_count = tokens[i].size; + out_data->meshes = options->memory_alloc(options->memory_user_data, sizeof(cgltf_mesh) * out_data->meshes_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->meshes_count; ++j) + { + i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_accessor(jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size accessor_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->accessors[accessor_index], 0, sizeof(cgltf_accessor)); + out_data->accessors[accessor_index].buffer_view = (void*)-1; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_data->accessors[accessor_index].buffer_view = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_data->accessors[accessor_index].offset = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 5120: + type = cgltf_component_type_r_8; + break; + case 5121: + type = cgltf_component_type_r_8u; + break; + case 5122: + type = cgltf_component_type_r_16; + break; + case 5123: + type = cgltf_component_type_r_16u; + break; + case 5125: + type = cgltf_component_type_r_32u; + break; + case 5126: + type = cgltf_component_type_r_32f; + break; + default: + type = cgltf_component_type_invalid; + break; + } + out_data->accessors[accessor_index].component_type = type; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_data->accessors[accessor_index].count = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_scalar; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_vec2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_vec3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_vec4; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_mat2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_mat3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) + { + out_data->accessors[accessor_index].type = cgltf_type_mat4; + } + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_rgba(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_rgba* out) +{ + int components = tokens[i].size; + if (components >= 2) { + out->r = cgltf_json_to_float(tokens + ++i, json_chunk); + out->g = cgltf_json_to_float(tokens + ++i, json_chunk); + + if (components > 2) + out->b = cgltf_json_to_float(tokens + ++i, json_chunk); + + if (components > 3) + out->a = cgltf_json_to_float(tokens + ++i, json_chunk); + } + else { + out->r = cgltf_json_to_float(tokens + ++i, json_chunk); + out->g = out->r; + out->b = out->r; + out->a = out->r; + } + + return ++i; +} + +static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out) { + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) + { + ++i; + out->texture = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + ++i; + out->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + +static int cgltf_parse_json_pbr(jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) + { + ++i; + out_data->materials[mat_index].pbr.metallic_factor = + cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + { + ++i; + out_data->materials[mat_index].pbr.roughness_factor = + cgltf_json_to_float(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) + { + i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk, + &(out_data->materials[mat_index].pbr.base_color)); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(out_data->materials[mat_index].pbr.base_color_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(out_data->materials[mat_index].pbr.metallic_roughness_texture)); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size img_index, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->images[img_index], 0, sizeof(cgltf_image)); + int size = tokens[i].size; + ++i; + + out_data->images[img_index].buffer_view = (void*)-1; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->images[img_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->images[img_index].uri, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->images[img_index].uri[strsize] = 0; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_data->images[img_index].buffer_view = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->images[img_index].mime_type = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->images[img_index].mime_type, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->images[img_index].mime_type[strsize] = 0; + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + +static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size smp_index, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->samplers[smp_index], 0, sizeof(cgltf_sampler)); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) + { + ++i; + out_data->samplers[smp_index].mag_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) + { + ++i; + out_data->samplers[smp_index].min_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) + { + ++i; + out_data->samplers[smp_index].wrap_s + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + { + ++i; + out_data->samplers[smp_index].wrap_t + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + + +static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size tex_index, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + memset(&out_data->textures[tex_index], 0, sizeof(cgltf_texture)); + out_data->textures[tex_index].image = (void*)-1; + out_data->textures[tex_index].sampler = (void*)-1; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) + { + ++i; + out_data->textures[tex_index].sampler + = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_data->textures[tex_index].image + = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + } + + return i; +} + +static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + cgltf_material* material = &out_data->materials[mat_index]; + + memset(material, 0, sizeof(cgltf_material)); + material->emissive_texture.texture = (void*)-1; + material->emissive_texture.scale = 1.0f; + + material->normal_texture.texture = (void*)-1; + material->normal_texture.scale = 1.0f; + + material->occlusion_texture.texture = (void*)-1; + material->occlusion_texture.scale = 1.0f; + + material->pbr.base_color_texture.texture = (void*)-1; + material->pbr.base_color_texture.scale = 1.0f; + + material->pbr.metallic_roughness_texture.texture = (void*)-1; + material->pbr.metallic_roughness_texture.scale = 1.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + material->name = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(material->name, + (const char*)json_chunk + tokens[i].start, + strsize); + material->name[strsize] = 0; + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) + { + i = cgltf_parse_json_pbr(tokens, i+1, json_chunk, mat_index, out_data); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) + { + i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk, + &(material->emissive_color)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(material->normal_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(material->emissive_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) + { + i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, + &(material->occlusion_texture)); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) + { + ++i; + material->double_sided = + cgltf_json_to_bool(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->accessors_count = tokens[i].size; + out_data->accessors = options->memory_alloc(options->memory_user_data, sizeof(cgltf_accessor) * out_data->accessors_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->accessors_count; ++j) + { + i = cgltf_parse_json_accessor(tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->materials_count = tokens[i].size; + out_data->materials = options->memory_alloc(options->memory_user_data, sizeof(cgltf_material) * out_data->materials_count); + ++i; + for (cgltf_size j = 0; j < out_data->materials_count; ++j) + { + i = cgltf_parse_json_material(options, tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->images_count = tokens[i].size; + out_data->images = options->memory_alloc(options->memory_user_data, sizeof(cgltf_image) * out_data->images_count); + ++i; + + for (cgltf_size j = 0; j < out_data->images_count; ++j) { + i = cgltf_parse_json_image(options, tokens, i, json_chunk, j, out_data); + if (i < 0) { + return i; + } + } + return i; +} + +static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->textures_count = tokens[i].size; + out_data->textures = options->memory_alloc(options->memory_user_data, sizeof(cgltf_texture) * out_data->textures_count); + ++i; + + for (cgltf_size j = 0; j < out_data->textures_count; ++j) { + i = cgltf_parse_json_texture(options, tokens, i, json_chunk, j, out_data); + if (i < 0) { + return i; + } + } + return i; +} + +static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->samplers_count = tokens[i].size; + out_data->samplers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_sampler) * out_data->samplers_count); + ++i; + + for (cgltf_size j = 0; j < out_data->samplers_count; ++j) { + i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, j, out_data); + if (i < 0) { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer_view(jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size buffer_view_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + memset(&out_data->buffer_views[buffer_view_index], 0, sizeof(cgltf_buffer_view)); + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].buffer = + (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].offset = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].size = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_data->buffer_views[buffer_view_index].stride = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 34962: + type = cgltf_buffer_view_type_vertices; + break; + case 34963: + type = cgltf_buffer_view_type_indices; + break; + default: + type = cgltf_buffer_view_type_invalid; + break; + } + out_data->buffer_views[buffer_view_index].type = type; + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->buffer_views_count = tokens[i].size; + out_data->buffer_views = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer_view) * out_data->buffer_views_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->buffer_views_count; ++j) + { + i = cgltf_parse_json_buffer_view(tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, + const uint8_t* json_chunk, cgltf_size buffer_index, + cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_data->buffers[buffer_index].uri = NULL; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_data->buffers[buffer_index].size = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) + { + ++i; + int strsize = tokens[i].end - tokens[i].start; + out_data->buffers[buffer_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1); + strncpy(out_data->buffers[buffer_index].uri, + (const char*)json_chunk + tokens[i].start, + strsize); + out_data->buffers[buffer_index].uri[strsize] = 0; + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + } + + return i; +} + +static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + out_data->buffers_count = tokens[i].size; + out_data->buffers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer) * out_data->buffers_count); + ++i; + for (cgltf_size j = 0 ; j < out_data->buffers_count; ++j) + { + i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, j, out_data); + if (i < 0) + { + return i; + } + } + return i; +} + +static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_type size = 0; + + switch (component_type) + { + case cgltf_component_type_rgb_32f: + size = 12; + break; + case cgltf_component_type_rgba_32f: + size = 16; + break; + case cgltf_component_type_rg_32f: + size = 8; + break; + case cgltf_component_type_rg_8: + size = 2; + break; + case cgltf_component_type_rg_16: + size = 4; + break; + case cgltf_component_type_rgba_8: + size = 4; + break; + case cgltf_component_type_rgba_16: + size = 8; + break; + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + size = 1; + break; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + size = 2; + break; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + size = 4; + break; + case cgltf_component_type_invalid: + default: + size = 0; + break; + } + + switch (type) + { + case cgltf_type_vec2: + size *= 2; + break; + case cgltf_type_vec3: + size *= 3; + break; + case cgltf_type_vec4: + size *= 4; + break; + case cgltf_type_mat2: + size *= 4; + break; + case cgltf_type_mat3: + size *= 9; + break; + case cgltf_type_mat4: + size *= 16; + break; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + size *= 1; + break; + } + + return size; +} + +cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data) +{ + jsmn_parser parser = {0}; + + if (options->json_token_count == 0) + { + options->json_token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); + } + + jsmntok_t* tokens = options->memory_alloc(options->memory_user_data, sizeof(jsmntok_t) * options->json_token_count); + + jsmn_init(&parser); + + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); + + if (token_count < 0 + || tokens[0].type != JSMN_OBJECT) + { + return cgltf_result_invalid_json; + } + + // The root is an object. + + for (int i = 1; i < token_count; ) + { + jsmntok_t const* tok = &tokens[i]; + if (tok->type == JSMN_STRING + && i + 1 < token_count) + { + int const name_length = tok->end - tok->start; + if (name_length == 6 + && strncmp((const char*)json_chunk + tok->start, "meshes", 6) == 0) + { + i = cgltf_parse_json_meshes(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 9 + && strncmp((const char*)json_chunk + tok->start, "accessors", 9) == 0) + { + i = cgltf_parse_json_accessors(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 11 + && strncmp((const char*)json_chunk + tok->start, "bufferViews", 11) == 0) + { + i = cgltf_parse_json_buffer_views(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 7 + && strncmp((const char*)json_chunk + tok->start, "buffers", 7) == 0) + { + i = cgltf_parse_json_buffers(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 9 + && strncmp((const char*)json_chunk + tok->start, "materials", 9) == 0) + { + i = cgltf_parse_json_materials(options, tokens, i+1, json_chunk, out_data); + } + else if (name_length == 6 + && strncmp((const char*)json_chunk + tok->start, "images", 6) == 0) + { + i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); + } + else if (name_length == 8 + && strncmp((const char*)json_chunk + tok->start, "textures", 8) == 0) + { + i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); + } + else if (name_length == 8 + && strncmp((const char*)json_chunk + tok->start, "samplers", 8) == 0) + { + i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return cgltf_result_invalid_json; + } + } + } + + options->memory_free(options->memory_user_data, tokens); + + /* Fix up pointers */ + for (cgltf_size i = 0; i < out_data->meshes_count; ++i) + { + for (cgltf_size j = 0; j < out_data->meshes[i].primitives_count; ++j) + { + if (out_data->meshes[i].primitives[j].indices ==(void*)-1) + { + out_data->meshes[i].primitives[j].indices = NULL; + } + else + { + out_data->meshes[i].primitives[j].indices + = &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].indices]; + } + + for (cgltf_size k = 0; k < out_data->meshes[i].primitives[j].attributes_count; ++k) + { + out_data->meshes[i].primitives[j].attributes[k].data + = &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].attributes[k].data]; + } + } + } + + for (cgltf_size i = 0; i < out_data->accessors_count; ++i) + { + if (out_data->accessors[i].buffer_view == (void*)-1) + { + out_data->accessors[i].buffer_view = NULL; + } + else + { + out_data->accessors[i].buffer_view + = &out_data->buffer_views[(cgltf_size)out_data->accessors[i].buffer_view]; + out_data->accessors[i].stride = 0; + if (out_data->accessors[i].buffer_view) + { + out_data->accessors[i].stride = out_data->accessors[i].buffer_view->stride; + } + } + if (out_data->accessors[i].stride == 0) + { + out_data->accessors[i].stride = cgltf_calc_size(out_data->accessors[i].type, out_data->accessors[i].component_type); + } + } + + for (cgltf_size i = 0; i < out_data->textures_count; ++i) + { + if (out_data->textures[i].image == (void*)-1) + { + out_data->textures[i].image = NULL; + } + else + { + out_data->textures[i].image = + &out_data->images[(cgltf_size)out_data->textures[i].image]; + } + + if (out_data->textures[i].sampler == (void*)-1) + { + out_data->textures[i].sampler = NULL; + } + else + { + out_data->textures[i].sampler = + &out_data->samplers[(cgltf_size)out_data->textures[i].sampler]; + } + } + + for (cgltf_size i = 0; i < out_data->images_count; ++i) + { + if (out_data->images[i].buffer_view == (void*)-1) + { + out_data->images[i].buffer_view = NULL; + } + else + { + out_data->images[i].buffer_view + = &out_data->buffer_views[(cgltf_size)out_data->images[i].buffer_view]; + } + } + + for (cgltf_size i = 0; i < out_data->materials_count; ++i) + { + if (out_data->materials[i].emissive_texture.texture == (void*)-1) + { + out_data->materials[i].emissive_texture.texture = NULL; + } + else + { + out_data->materials[i].emissive_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].emissive_texture.texture]; + } + + if (out_data->materials[i].normal_texture.texture == (void*)-1) + { + out_data->materials[i].normal_texture.texture = NULL; + } + else + { + out_data->materials[i].normal_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].normal_texture.texture]; + } + + if (out_data->materials[i].occlusion_texture.texture == (void*)-1) + { + out_data->materials[i].occlusion_texture.texture = NULL; + } + else + { + out_data->materials[i].occlusion_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].occlusion_texture.texture]; + } + + if (out_data->materials[i].pbr.base_color_texture.texture == (void*)-1) + { + out_data->materials[i].pbr.base_color_texture.texture = NULL; + } + else + { + out_data->materials[i].pbr.base_color_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].pbr.base_color_texture.texture]; + } + + if (out_data->materials[i].pbr.metallic_roughness_texture.texture == (void*)-1) + { + out_data->materials[i].pbr.metallic_roughness_texture.texture = NULL; + } + else + { + out_data->materials[i].pbr.metallic_roughness_texture.texture = + &out_data->textures[(cgltf_size)out_data->materials[i].pbr.metallic_roughness_texture.texture]; + } + } + + for (cgltf_size i = 0; i < out_data->buffer_views_count; ++i) + { + out_data->buffer_views[i].buffer + = &out_data->buffers[(cgltf_size)out_data->buffer_views[i].buffer]; + } + + return cgltf_result_success; +} + +/* + * -- jsmn.c start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +/* + * -- jsmn.c end -- + */ + +#endif /* #ifdef CGLTF_IMPLEMENTATION */ + +#ifdef __cplusplus +} +#endif diff --git a/src/external/dirent.h b/src/external/dirent.h new file mode 100644 index 000000000..a955253e0 --- /dev/null +++ b/src/external/dirent.h @@ -0,0 +1,198 @@ +#ifndef DIRENT_H +#define DIRENT_H + +/* + + Declaration of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003. + Rights: See end of file. + +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct DIR DIR; + +struct dirent +{ + char *d_name; +}; + +DIR *opendir(const char *); +int closedir(DIR *); +struct dirent *readdir(DIR *); +void rewinddir(DIR *); + +/* + + Copyright Kevlin Henney, 1997, 2003. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ + +#ifdef __cplusplus +} +#endif + +#endif // DIRENT_H + +/* + + Implementation of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003 and July 2012. + Rights: See end of file. + +*/ + +#include +#include /* _findfirst and _findnext set errno iff they return -1 */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ + +struct DIR +{ + handle_type handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ +}; + +DIR *opendir(const char *name) +{ + DIR *dir = 0; + + if(name && name[0]) + { + size_t base_length = strlen(name); + const char *all = /* search pattern must end with suitable wildcard */ + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if((dir = (DIR *) malloc(sizeof *dir)) != 0 && + (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) + { + strcat(strcpy(dir->name, name), all); + + if((dir->handle = + (handle_type) _findfirst(dir->name, &dir->info)) != -1) + { + dir->result.d_name = 0; + } + else /* rollback */ + { + free(dir->name); + free(dir); + dir = 0; + } + } + else /* rollback */ + { + free(dir); + dir = 0; + errno = ENOMEM; + } + } + else + { + errno = EINVAL; + } + + return dir; +} + +int closedir(DIR *dir) +{ + int result = -1; + + if(dir) + { + if(dir->handle != -1) + { + result = _findclose(dir->handle); + } + + free(dir->name); + free(dir); + } + + if(result == -1) /* map all errors to EBADF */ + { + errno = EBADF; + } + + return result; +} + +struct dirent *readdir(DIR *dir) +{ + struct dirent *result = 0; + + if(dir && dir->handle != -1) + { + if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) + { + result = &dir->result; + result->d_name = dir->info.name; + } + } + else + { + errno = EBADF; + } + + return result; +} + +void rewinddir(DIR *dir) +{ + if(dir && dir->handle != -1) + { + _findclose(dir->handle); + dir->handle = (handle_type) _findfirst(dir->name, &dir->info); + dir->result.d_name = 0; + } + else + { + errno = EBADF; + } +} + +#ifdef __cplusplus +} +#endif + +/* + + Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index 467d319da..cd8920a14 100644 --- a/src/external/dr_mp3.h +++ b/src/external/dr_mp3.h @@ -1,5 +1,5 @@ // MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_mp3 - v0.2.5 - 2018-06-22 +// dr_mp3 - v0.3.2 - 2018-09-11 // // David Reid - mackron@gmail.com // @@ -99,28 +99,26 @@ typedef drmp3_uint32 drmp3_bool32; // ================== typedef struct { - int frame_bytes; - int channels; - int hz; - int layer; - int bitrate_kbps; + int frame_bytes, channels, hz, layer, bitrate_kbps; } drmp3dec_frame_info; typedef struct { - float mdct_overlap[2][9*32]; - float qmf_state[15*2*32]; - int reserv; - int free_format_bytes; - unsigned char header[4]; - unsigned char reserv_buf[511]; + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + unsigned char header[4], reserv_buf[511]; } drmp3dec; // Initializes a low level decoder. void drmp3dec_init(drmp3dec *dec); // Reads a frame from a low level decoder. -int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info); +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); + +// Helper for converting between f32 and s16. +void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples); + + // Main API (Pull API) @@ -220,7 +218,7 @@ typedef struct drmp3_uint32 frameSampleRate; // The sample rate of the currently loaded MP3 frame. Internal use only. drmp3_uint32 framesConsumed; drmp3_uint32 framesRemaining; - drmp3_int16 frames[DRMP3_MAX_SAMPLES_PER_FRAME]; + drmp3_uint8 frames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; // <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. drmp3_src src; size_t dataSize; size_t dataCapacity; @@ -314,8 +312,12 @@ void drmp3_free(void* p); #define DR_MP3_NO_SIMD #endif +#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) + #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES #define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#endif #define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ @@ -361,7 +363,7 @@ void drmp3_free(void* p); #if defined(_MSC_VER) #include #endif -#include +#include #define DRMP3_HAVE_SSE 1 #define DRMP3_HAVE_SIMD 1 #define DRMP3_VSTORE _mm_storeu_ps @@ -462,44 +464,27 @@ static int drmp3_have_simd() typedef struct { const drmp3_uint8 *buf; - int pos; - int limit; + int pos, limit; } drmp3_bs; typedef struct { - drmp3_uint8 total_bands; - drmp3_uint8 stereo_bands; - drmp3_uint8 bitalloc[64]; - drmp3_uint8 scfcod[64]; float scf[3*64]; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; } drmp3_L12_scale_info; typedef struct { - drmp3_uint8 tab_offset; - drmp3_uint8 code_tab_width; - drmp3_uint8 band_count; + drmp3_uint8 tab_offset, code_tab_width, band_count; } drmp3_L12_subband_alloc; typedef struct { const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length; - drmp3_uint16 big_values; - drmp3_uint16 scalefac_compress; - drmp3_uint8 global_gain; - drmp3_uint8 block_type; - drmp3_uint8 mixed_block_flag; - drmp3_uint8 n_long_sfb; - drmp3_uint8 n_short_sfb; - drmp3_uint8 table_select[3]; - drmp3_uint8 region_count[3]; - drmp3_uint8 subblock_gain[3]; - drmp3_uint8 preflag; - drmp3_uint8 scalefac_scale; - drmp3_uint8 count1_table; - drmp3_uint8 scfsi; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; } drmp3_L3_gr_info; typedef struct @@ -507,10 +492,8 @@ typedef struct drmp3_bs bs; drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; drmp3_L3_gr_info gr_info[4]; - float grbuf[2][576]; - float scf[40]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; drmp3_uint8 ist_pos[2][39]; - float syn[18 + 15][2*32]; } drmp3dec_scratch; static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) @@ -988,17 +971,19 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is } } +static const float g_drmp3_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + static float drmp3_L3_pow_43(int x) { - static const float g_pow43[129] = { - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f - }; float frac; int sign, mult = 256; if (x < 129) { - return g_pow43[x]; + return g_drmp3_pow43[16 + x]; } if (x < 1024) @@ -1009,12 +994,11 @@ static float drmp3_L3_pow_43(int x) sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_pow43[(x + sign) >> 6]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { - static const float g_pow43_signed[32] = { 0,0,1,-1,2.519842f,-2.519842f,4.326749f,-4.326749f,6.349604f,-6.349604f,8.549880f,-8.549880f,10.902724f,-10.902724f,13.390518f,-13.390518f,16.000000f,-16.000000f,18.720754f,-18.720754f,21.544347f,-21.544347f,24.463781f,-24.463781f,27.473142f,-27.473142f,30.567351f,-30.567351f,33.741992f,-33.741992f,36.993181f,-36.993181f }; static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, @@ -1053,7 +1037,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; - const short *codebook = tabs + tabindex[tab_num]; + const drmp3_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; do { @@ -1083,7 +1067,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g *dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1); } else { - *dst = g_pow43_signed[lsb*2 + (bs_cache >> 31)]*one; + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } DRMP3_FLUSH_BITS(lsb ? 1 : 0); } @@ -1659,18 +1643,27 @@ static void drmp3d_DCT_II(float *grbuf, int n) #endif } -static short drmp3d_scale_pcm(float sample) -{ - if (sample > 32767.0) return (short) 32767; - if (sample < -32768.0) return (short)-32768; - int s = (int)(sample + .5f); - s -= (s < 0); /* away from zero, to be compliant */ - if (s > 32767) return (short) 32767; - if (s < -32768) return (short)-32768; - return (short)s; -} +#ifndef DR_MP3_FLOAT_OUTPUT +typedef drmp3_int16 drmp3d_sample_t; -static void drmp3d_synth_pair(short *pcm, int nch, const float *z) +static drmp3_int16 drmp3d_scale_pcm(float sample) +{ + if (sample >= 32766.5) return (drmp3_int16) 32767; + if (sample <= -32767.5) return (drmp3_int16)-32768; + drmp3_int16 s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + return (drmp3_int16)s; +} +#else +typedef float drmp3d_sample_t; + +static float drmp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif + +static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) { float a; a = (z[14*64] - z[ 0]) * 29; @@ -1695,11 +1688,11 @@ static void drmp3d_synth_pair(short *pcm, int nch, const float *z) pcm[16*nch] = drmp3d_scale_pcm(a); } -static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) +static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) { int i; float *xr = xl + 576*(nch - 1); - short *dstr = dstl + (nch - 1); + drmp3d_sample_t *dstr = dstl + (nch - 1); static const float g_win[] = { -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, @@ -1756,19 +1749,20 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) { +#ifndef DR_MP3_FLOAT_OUTPUT #if DRMP3_HAVE_SSE static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 6); + dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); #else int16x4_t pcma, pcmb; a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); @@ -1784,6 +1778,30 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif +#else + static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + a = DRMP3_VMUL(a, g_scale); + b = DRMP3_VMUL(b, g_scale); +#if DRMP3_HAVE_SSE + _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); +#else + vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); + vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); + vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); + vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); + vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); + vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); + vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); + vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); +#endif +#endif /* DR_MP3_FLOAT_OUTPUT */ } } else #endif @@ -1821,7 +1839,7 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) #endif } -static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, short *pcm, float *lins) +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) { int i; for (i = 0; i < nch; i++) @@ -1906,7 +1924,7 @@ void drmp3dec_init(drmp3dec *dec) dec->header[0] = 0; } -int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info) +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) { int i = 0, igr, frame_size = 0, success = 1; const drmp3_uint8 *hdr; @@ -1940,6 +1958,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + if (!pcm) + { + return drmp3_hdr_frame_samples(hdr); + } + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); if (DRMP3_HDR_IS_CRC(hdr)) { @@ -1957,11 +1980,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); if (success) { - for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) { memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); } } drmp3_L3_save_reservoir(dec, &scratch); @@ -1980,9 +2003,9 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes { i = 0; drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); - pcm += 384*info->channels; + pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) { @@ -1995,6 +2018,64 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes return success*drmp3_hdr_frame_samples(dec->header); } +void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples) +{ + if(num_samples > 0) + { + int i = 0; +#if DRMP3_HAVE_SIMD + int aligned_count = num_samples & ~7; + for(; i < aligned_count; i+=8) + { + static const drmp3_f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f }; + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), g_scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), g_scale); +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); +#endif + } +#endif + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (drmp3_int16) 32767; + else if (sample <= -32767.5) + out[i] = (drmp3_int16)-32768; + else + { + short s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } + } +} @@ -2004,6 +2085,16 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes // /////////////////////////////////////////////////////////////////////////////// +#if defined(SIZE_MAX) + #define DRMP3_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRMP3_SIZE_MAX 0xFFFFFFFF + #endif +#endif + // Options. #ifndef DR_MP3_DEFAULT_CHANNELS #define DR_MP3_DEFAULT_CHANNELS 2 @@ -2311,8 +2402,10 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { - pMP3->atEnd = DRMP3_TRUE; - return DRMP3_FALSE; // No data. + if (pMP3->dataSize == 0) { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; // No data. + } } pMP3->dataSize += bytesRead; @@ -2324,7 +2417,7 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) } drmp3dec_frame_info info; - drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above. + drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, (drmp3d_sample_t*)pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above. if (samplesRead != 0) { size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); for (size_t i = 0; i < leftoverDataSize; ++i) { @@ -2377,28 +2470,54 @@ static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, voi while (frameCount > 0) { // Read from the in-memory buffer first. while (pMP3->framesRemaining > 0 && frameCount > 0) { + drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->frames; +#ifndef DR_MP3_FLOAT_OUTPUT if (pMP3->frameChannels == 1) { if (pMP3->channels == 1) { // Mono -> Mono. - pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f; } else { // Mono -> Stereo. - pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; - pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[1] = frames[pMP3->framesConsumed] / 32768.0f; } } else { if (pMP3->channels == 1) { // Stereo -> Mono float sample = 0; - sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; - sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; pFramesOutF[0] = sample * 0.5f; } else { // Stereo -> Stereo - pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; - pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; } } +#else + if (pMP3->frameChannels == 1) { + if (pMP3->channels == 1) { + // Mono -> Mono. + pFramesOutF[0] = frames[pMP3->framesConsumed]; + } else { + // Mono -> Stereo. + pFramesOutF[0] = frames[pMP3->framesConsumed]; + pFramesOutF[1] = frames[pMP3->framesConsumed]; + } + } else { + if (pMP3->channels == 1) { + // Stereo -> Mono + float sample = 0; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0]; + sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1]; + pFramesOutF[0] = sample * 0.5f; + } else { + // Stereo -> Stereo + pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0]; + pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1]; + } + } +#endif pMP3->framesConsumed += 1; pMP3->framesRemaining -= 1; @@ -2466,11 +2585,13 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek srcConfig.channels = pMP3->channels; srcConfig.algorithm = drmp3_src_algorithm_linear; if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) { + drmp3_uninit(pMP3); return DRMP3_FALSE; } // Decode the first frame to confirm that it is indeed a valid MP3 stream. if (!drmp3_decode_next_frame(pMP3)) { + drmp3_uninit(pMP3); return DRMP3_FALSE; // Not a valid MP3 stream. } @@ -2681,7 +2802,7 @@ float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp } drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float); - if (newFramesBufferSize > SIZE_MAX) { + if (newFramesBufferSize > DRMP3_SIZE_MAX) { break; } @@ -2771,6 +2892,37 @@ void drmp3_free(void* p) // REVISION HISTORY // =============== // +// v0.3.2 - 2018-09-11 +// - Fix a couple of memory leaks. +// - Bring up to date with minimp3. +// +// v0.3.1 - 2018-08-25 +// - Fix C++ build. +// +// v0.3.0 - 2018-08-25 +// - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has +// been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or +// not the DR_MP3_FLOAT_OUTPUT option is set. +// +// v0.2.11 - 2018-08-08 +// - Fix a bug where the last part of a file is not read. +// +// v0.2.10 - 2018-08-07 +// - Improve 64-bit detection. +// +// v0.2.9 - 2018-08-05 +// - Fix C++ build on older versions of GCC. +// - Bring up to date with minimp3. +// +// v0.2.8 - 2018-08-02 +// - Fix compilation errors with older versions of GCC. +// +// v0.2.7 - 2018-07-13 +// - Bring up to date with minimp3. +// +// v0.2.6 - 2018-07-12 +// - Bring up to date with minimp3. +// // v0.2.5 - 2018-06-22 // - Bring up to date with minimp3. // diff --git a/src/external/glfw/include/GLFW/glfw3native.h b/src/external/glfw/include/GLFW/glfw3native.h index 4372cb766..d585496c9 100644 --- a/src/external/glfw/include/GLFW/glfw3native.h +++ b/src/external/glfw/include/GLFW/glfw3native.h @@ -90,13 +90,21 @@ extern "C" { #undef APIENTRY #undef GLFW_APIENTRY_DEFINED #endif - #include +// RAY: Actually, only HWND handler needs to be defined +// Including windows.h could suppose symbols re-definition issues (i.e Rectangle type) +//#include + typedef void *PVOID; + typedef PVOID HANDLE; + typedef HANDLE HWND; #elif defined(GLFW_EXPOSE_NATIVE_COCOA) #include #if defined(__OBJC__) #import #else - typedef void* id; + // RAY: Added protection in case OBJC types defined + #if !OBJC_TYPES_DEFINED + typedef void* id; + #endif #endif #elif defined(GLFW_EXPOSE_NATIVE_X11) #include diff --git a/src/external/mini_al.c b/src/external/mini_al.c index 1ce944190..eabb63654 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 4314343de..759662c7b 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.8 - 2018-09-14 // // David Reid - davidreidsoftware@gmail.com @@ -20,7 +20,9 @@ // - ALSA // - PulseAudio // - JACK -// - OSS +// - sndio (OpenBSD) +// - audio(4) (NetBSD and OpenBSD) +// - 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,18 +118,19 @@ // - 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. +// - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it. +// - If you are using the platform's default device, mini_al will try automatically switching the internal +// device when the device is unplugged. This feature is disabled when the device is opened in exclusive +// mode. // // // // BACKEND NUANCES // =============== // -// DirectSound -// ----------- -// - DirectSound currently supports a maximum of 4 periods. -// // Android // ------- // - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: @@ -136,7 +139,6 @@ // // UWP // --- -// - UWP is only supported when compiling as C++. // - UWP only supports default playback and capture devices. // - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): // @@ -146,14 +148,6 @@ // // // -// PulseAudio -// ---------- -// - Each device has it's own dedicated main loop. -// -// JACK -// ---- -// - It's possible for mal_device.bufferSizeInFrames to change during run time. -// // // OPTIONS // ======= @@ -180,6 +174,12 @@ // #define MAL_NO_COREAUDIO // Disables the Core Audio backend. // +// #define MAL_NO_SNDIO +// Disables the sndio backend. +// +// #define MAL_NO_AUDIO4 +// Disables the audio(4) backend. +// // #define MAL_NO_OSS // Disables the OSS backend. // @@ -200,13 +200,16 @@ // // #define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY // #define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE -// When a buffer size of 0 is specified when a device is initialized it will default to a buffer of this size (depending -// on the chosen performance profile) multiplied by a weight which is calculated at run-time. These can be increased or -// decreased depending on your specific requirements. +// When a buffer size of 0 is specified when a device is initialized it will default to a buffer of this size, depending +// on the chosen performance profile. These can be increased or decreased depending on your specific requirements. // // #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. // @@ -231,6 +234,9 @@ // // #define MAL_DEBUT_OUTPUT // Enable printf() debug output. +// +// #ifndef MAL_COINIT_VALUE +// Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED. #ifndef mini_al_h #define mini_al_h @@ -248,7 +254,9 @@ extern "C" { // Platform/backend detection. #ifdef _WIN32 #define MAL_WIN32 - #if (!defined(WINAPI_FAMILY) || WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + #define MAL_WIN32_UWP + #else #define MAL_WIN32_DESKTOP #endif #else @@ -275,82 +283,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 // For size_t. #ifndef MAL_HAS_STDINT @@ -421,9 +353,6 @@ typedef void* mal_handle; typedef void* mal_ptr; typedef void (* mal_proc)(void); -typedef struct mal_context mal_context; -typedef struct mal_device mal_device; - #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef mal_uint16 wchar_t; #endif @@ -478,122 +407,8 @@ typedef mal_uint16 wchar_t; #define MAL_LOG_LEVEL MAL_LOG_LEVEL_ERROR #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 struct mal_context mal_context; +typedef struct mal_device mal_device; typedef mal_uint8 mal_channel; #define MAL_CHANNEL_NONE 0 @@ -687,39 +502,36 @@ typedef int mal_result; #define MAL_INVALID_DEVICE_CONFIG -31 #define MAL_ACCESS_DENIED -32 #define MAL_TOO_LARGE -33 +#define MAL_DEVICE_UNAVAILABLE -34 +#define MAL_TIMEOUT -35 -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); +// 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_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; - -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 +578,8 @@ typedef enum mal_standard_channel_map_rfc3551, // Based off AIFF. mal_standard_channel_map_flac, mal_standard_channel_map_vorbis, + mal_standard_channel_map_sound4, // FreeBSD's sound(4). + 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 +589,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); @@ -929,6 +677,12 @@ typedef enum mal_src_sinc_window_function_default = mal_src_sinc_window_function_hann } mal_src_sinc_window_function; +typedef struct +{ + mal_src_sinc_window_function windowFunction; + mal_uint32 windowWidth; +} mal_src_config_sinc; + typedef struct { mal_uint32 sampleRateIn; @@ -944,11 +698,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 +757,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 +779,647 @@ 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__) || defined(__OpenBSD__) + #define MAL_SUPPORT_AUDIO4 // Only support audio(4) 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_AUDIO4) && defined(MAL_SUPPORT_AUDIO4) + #define MAL_ENABLE_AUDIO4 +#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 + +#ifdef MAL_SUPPORT_WASAPI +// We need a IMMNotificationClient object for WASAPI. +typedef struct +{ + void* lpVtbl; + mal_uint32 counter; + mal_device* pDevice; +} mal_IMMNotificationClient; +#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_audio4, + 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_AUDIO4 + char audio4[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; @@ -1040,6 +1427,7 @@ typedef struct mal_uint32 sampleRate; mal_channel channelMap[MAL_MAX_CHANNELS]; mal_uint32 bufferSizeInFrames; + mal_uint32 bufferSizeInMilliseconds; mal_uint32 periods; mal_share_mode shareMode; mal_performance_profile performanceProfile; @@ -1102,6 +1490,7 @@ struct mal_context mal_result (* onGetDeviceInfo )(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo); mal_result (* onDeviceInit )(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice); void (* onDeviceUninit )(mal_device* pDevice); + mal_result (* onDeviceReinit )(mal_device* pDevice); mal_result (* onDeviceStart )(mal_device* pDevice); mal_result (* onDeviceStop )(mal_device* pDevice); mal_result (* onDeviceBreakMainLoop)(mal_device* pDevice); @@ -1285,6 +1674,7 @@ struct mal_context mal_proc AudioObjectGetPropertyData; mal_proc AudioObjectGetPropertyDataSize; mal_proc AudioObjectSetPropertyData; + mal_proc AudioObjectAddPropertyListener; mal_handle hAudioUnit; // Could possibly be set to AudioToolbox on later versions of macOS. mal_proc AudioComponentFindNext; @@ -1299,6 +1689,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_AUDIO4 + struct + { + int _unused; + } audio4; +#endif #ifdef MAL_SUPPORT_OSS struct { @@ -1482,19 +1901,21 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device mal_uint32 sampleRate; mal_channel channelMap[MAL_MAX_CHANNELS]; mal_uint32 bufferSizeInFrames; + mal_uint32 bufferSizeInMilliseconds; mal_uint32 periods; mal_uint32 state; mal_recv_proc onRecv; mal_send_proc onSend; mal_stop_proc onStop; - void* pUserData; // Application defined data. + void* pUserData; // Application defined data. char name[256]; + mal_device_config initConfig; // The configuration passed in to mal_device_init(). Mainly used for reinitializing the backend device. mal_mutex lock; mal_event wakeupEvent; mal_event startEvent; mal_event stopEvent; mal_thread thread; - mal_result workResult; // This is set by the worker thread after it's finished doing a job. + mal_result workResult; // This is set by the worker thread after it's finished doing a job. mal_bool32 usingDefaultFormat : 1; mal_bool32 usingDefaultChannels : 1; mal_bool32 usingDefaultSampleRate : 1; @@ -1503,6 +1924,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device mal_bool32 usingDefaultPeriods : 1; mal_bool32 exclusiveMode : 1; mal_bool32 isOwnerOfContext : 1; // When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into mal_device_init(). + mal_bool32 isDefaultDevice : 1; // Used to determine if the backend should try reinitializing if the default device is unplugged. mal_format internalFormat; mal_uint32 internalChannels; mal_uint32 internalSampleRate; @@ -1519,9 +1941,12 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device /*IAudioClient**/ mal_ptr pAudioClient; /*IAudioRenderClient**/ mal_ptr pRenderClient; /*IAudioCaptureClient**/ mal_ptr pCaptureClient; + /*IMMDeviceEnumerator**/ mal_ptr pDeviceEnumerator; /* <-- Used for IMMNotificationClient notifications. Required for detecting default device changes. */ + mal_IMMNotificationClient notificationClient; /*HANDLE*/ mal_handle hEvent; - /*HANDLE*/ mal_handle hStopEvent; + /*HANDLE*/ mal_handle hBreakEvent; /* <-- Used to break from WaitForMultipleObjects() in the main loop. */ mal_bool32 breakFromMainLoop; + mal_bool32 hasDefaultDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */ } wasapi; #endif #ifdef MAL_SUPPORT_DSOUND @@ -1589,8 +2014,27 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device /*AudioComponent*/ mal_ptr component; // <-- Can this be per-context? /*AudioUnit*/ mal_ptr audioUnit; /*AudioBufferList**/ mal_ptr pAudioBufferList; // Only used for input devices. + mal_bool32 isSwitchingDevice; /* <-- Set to true when the default device has changed and mini_al is in the process of switching. */ } coreaudio; #endif +#ifdef MAL_SUPPORT_SNDIO + struct + { + mal_ptr handle; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } sndio; +#endif +#ifdef MAL_SUPPORT_AUDIO4 + struct + { + int fd; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } audio4; +#endif #ifdef MAL_SUPPORT_OSS struct { @@ -1663,7 +2107,9 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device // - WASAPI // - DirectSound // - WinMM -// - Core Audio (macOS, iOS) +// - Core Audio (Apple) +// - sndio +// - audio(4) // - OSS // - PulseAudio // - ALSA @@ -1791,9 +2237,11 @@ mal_result mal_context_get_device_info(mal_context* pContext, mal_device_type ty // // Passing in 0 to any property in pConfig will force the use of a default value. In the case of // sample format, channel count, sample rate and channel map it will default to the values used by -// the backend's internal device. If is 0, it will default to -// MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS. If is set to 0 it will default to -// MAL_DEFAULT_PERIODS. +// the backend's internal device. For the size of the buffer you can set bufferSizeInFrames or +// bufferSizeInMilliseconds (if both are set it will prioritize bufferSizeInFrames). If both are +// set to zero, it will default to MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY or +// MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE, depending on whether or not performanceProfile +// is set to mal_performance_profile_low_latency or mal_performance_profile_conservative. // // When sending or receiving data to/from a device, mini_al will internally perform a format // conversion to convert between the format specified by pConfig and the format used internally by @@ -1840,6 +2288,8 @@ void mal_device_uninit(mal_device* pDevice); // // Thread Safety: SAFE // This API is implemented as a simple atomic assignment. +// +// DEPRECATED. Set this when the device is initialized with mal_device_init*(). void mal_device_set_recv_callback(mal_device* pDevice, mal_recv_proc proc); // Sets the callback to use when the application needs to send data to the device for playback. @@ -1850,6 +2300,8 @@ void mal_device_set_recv_callback(mal_device* pDevice, mal_recv_proc proc); // // Thread Safety: SAFE // This API is implemented as a simple atomic assignment. +// +// DEPRECATED. Set this when the device is initialized with mal_device_init*(). void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc); // Sets the callback to use when the device has stopped, either explicitly or as a result of an error. @@ -1941,15 +2393,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 +2487,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,81 +2509,29 @@ 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. -// -// This is a slow API because it performs a profiling test. -float mal_calculate_cpu_speed_factor(void); - // Adjust buffer size based on a scaling factor. // // This just multiplies the base size by the scaling factor, making sure it's a size of at least 1. mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale); +// Calculates a buffer size in milliseconds from the specified number of frames and sample rate. +mal_uint32 mal_calculate_buffer_size_in_milliseconds_from_frames(mal_uint32 bufferSizeInFrames, mal_uint32 sampleRate); + +// Calculates a buffer size in frames from the specified number of milliseconds and sample rate. +mal_uint32 mal_calculate_buffer_size_in_frames_from_milliseconds(mal_uint32 bufferSizeInMilliseconds, mal_uint32 sampleRate); + +// Retrieves the default buffer size in milliseconds based on the specified performance profile. +mal_uint32 mal_get_default_buffer_size_in_milliseconds(mal_performance_profile performanceProfile); + // 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); +mal_uint32 mal_get_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate); +#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 +2556,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 @@ -2477,8 +2645,9 @@ typedef struct double time; } mal_sine_wave; -mal_result mal_sine_wave_init(double amplitude, double period, mal_uint32 sampleRate, mal_sine_wave* pSignWave); -mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float* pSamples); +mal_result mal_sine_wave_init(double amplitude, double period, mal_uint32 sampleRate, mal_sine_wave* pSineWave); +mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* pSamples); +mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount, mal_uint32 channels, mal_stream_layout layout, float** ppFrames); #ifdef __cplusplus @@ -2660,9 +2829,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) @@ -2752,7 +2935,7 @@ static MAL_INLINE mal_bool32 mal_has_avx2() #if defined(_AVX2_) || defined(__AVX2__) return MAL_TRUE; // If the compiler is allowed to freely generate AVX2 code we can assume support. #else - // AVX requires both CPU and OS support. + // AVX2 requires both CPU and OS support. #if defined(MAL_NO_CPUID) || defined(MAL_NO_XGETBV) return MAL_FALSE; #else @@ -2773,7 +2956,7 @@ static MAL_INLINE mal_bool32 mal_has_avx2() #endif #endif #else - return MAL_FALSE; // AVX is only supported on x86 and x64 architectures. + return MAL_FALSE; // AVX2 is only supported on x86 and x64 architectures. #endif #else return MAL_FALSE; // No compiler support. @@ -2834,6 +3017,28 @@ 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_COINIT_VALUE +#define MAL_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED*/ +#endif + + + #ifndef MAL_PI #define MAL_PI 3.14159265358979323846264f #endif @@ -2847,136 +3052,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() - #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() - #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() - #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() - #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() - #undef MAL_HAS_SDL - #endif - #else - #if !__has_include() - #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 @@ -3000,12 +3075,12 @@ typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, // The base buffer size in milliseconds for low latency mode. #ifndef MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY -#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 +#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY 25 #endif // The base buffer size in milliseconds for conservative mode. #ifndef MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE -#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE 50 +#define MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE 150 #endif @@ -3032,8 +3107,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 +3118,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 +3643,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() + #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() + #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() + #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_AUDIO4 + #define MAL_HAS_AUDIO4 // When enabled, always assume audio(4) 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() + #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() + #undef MAL_HAS_SDL + #endif + #else + #if !__has_include() + #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_audio4, + 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_audio4: return "audio(4)"; + 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 +4429,46 @@ mal_uint32 mal_get_closest_standard_sample_rate(mal_uint32 sampleRateIn) } +mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale) +{ + return mal_max(1, (mal_uint32)(baseBufferSize*scale)); +} + +mal_uint32 mal_calculate_buffer_size_in_milliseconds_from_frames(mal_uint32 bufferSizeInFrames, mal_uint32 sampleRate) +{ + return bufferSizeInFrames / (sampleRate/1000); +} + +mal_uint32 mal_calculate_buffer_size_in_frames_from_milliseconds(mal_uint32 bufferSizeInMilliseconds, mal_uint32 sampleRate) +{ + return bufferSizeInMilliseconds * (sampleRate/1000); +} + +mal_uint32 mal_get_default_buffer_size_in_milliseconds(mal_performance_profile performanceProfile) +{ + if (performanceProfile == mal_performance_profile_low_latency) { + return MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY; + } else { + return MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; + } +} + +mal_uint32 mal_get_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate) +{ + mal_uint32 bufferSizeInMilliseconds = mal_get_default_buffer_size_in_milliseconds(performanceProfile); + if (bufferSizeInMilliseconds == 0) { + bufferSizeInMilliseconds = 1; + } + + mal_uint32 sampleRateMS = (sampleRate/1000); + if (sampleRateMS == 0) { + sampleRateMS = 1; + } + + return bufferSizeInMilliseconds * sampleRateMS; +} + + const char* mal_log_level_to_string(mal_uint32 logLevel) { switch (logLevel) @@ -4403,6 +4718,7 @@ mal_uint32 mal_get_format_priority_index(mal_format format) // Lower = better. return (mal_uint32)-1; } +void mal_device__post_init_setup(mal_device* pDevice); /////////////////////////////////////////////////////////////////////////////// // @@ -4491,19 +4807,21 @@ mal_result mal_device_init__null(mal_context* pContext, mal_device_type type, co (void)pContext; (void)type; (void)pDeviceID; + (void)pConfig; mal_assert(pDevice != NULL); mal_zero_object(&pDevice->null_device); - pDevice->bufferSizeInFrames = pConfig->bufferSizeInFrames; - pDevice->periods = pConfig->periods; - if (type == mal_device_type_playback) { mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "NULL Playback Device", (size_t)-1); } else { mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "NULL Capture Device", (size_t)-1); } + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + } + pDevice->null_device.pBuffer = (mal_uint8*)mal_malloc(pDevice->bufferSizeInFrames * pDevice->channels * mal_get_bytes_per_sample(pDevice->format)); if (pDevice->null_device.pBuffer == NULL) { return MAL_OUT_OF_MEMORY; @@ -5001,19 +5319,27 @@ static MAL_INLINE void mal_PropVariantInit(PROPVARIANT* pProp) mal_zero_object(pProp); } -const PROPERTYKEY MAL_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; -const PROPERTYKEY MAL_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; -const IID MAL_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; // 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) -const IID MAL_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) -const IID MAL_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) +const PROPERTYKEY MAL_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; +const PROPERTYKEY MAL_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; + +const IID MAL_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; // 00000000-0000-0000-C000-000000000046 +const IID MAL_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; // 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 + +const IID MAL_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; // 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) +const IID MAL_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; // 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) +const IID MAL_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; // 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) +const IID MAL_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) +const IID MAL_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) +const IID MAL_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; // 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) #ifndef MAL_WIN32_DESKTOP -const IID MAL_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; // E6327CAD-DCEC-4949-AE8A-991E976A79D2 -const IID MAL_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; // 2EEF81BE-33FA-4800-9670-1CD474972C3F +const IID MAL_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; // E6327CAD-DCEC-4949-AE8A-991E976A79D2 +const IID MAL_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; // 2EEF81BE-33FA-4800-9670-1CD474972C3F +const IID MAL_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; // 41D949AB-9862-444A-80F6-C261334DA5EB #endif -const IID MAL_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; // BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) -const IID MAL_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; // A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) +const IID MAL_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; // BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) +const IID MAL_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; // A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) #ifdef __cplusplus #define MAL_CLSID_MMDeviceEnumerator MAL_CLSID_MMDeviceEnumerator_Instance #define MAL_IID_IMMDeviceEnumerator MAL_IID_IMMDeviceEnumerator_Instance @@ -5022,6 +5348,7 @@ const IID MAL_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F3 #define MAL_IID_IMMDeviceEnumerator &MAL_IID_IMMDeviceEnumerator_Instance #endif +typedef struct mal_IUnknown mal_IUnknown; #ifdef MAL_WIN32_DESKTOP #define MAL_MM_DEVICE_STATE_ACTIVE 1 #define MAL_MM_DEVICE_STATE_DISABLED 2 @@ -5031,12 +5358,14 @@ const IID MAL_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F3 typedef struct mal_IMMDeviceEnumerator mal_IMMDeviceEnumerator; typedef struct mal_IMMDeviceCollection mal_IMMDeviceCollection; typedef struct mal_IMMDevice mal_IMMDevice; -typedef struct mal_IMMNotificationClient mal_IMMNotificationClient; #else +typedef struct mal_IActivateAudioInterfaceCompletionHandler mal_IActivateAudioInterfaceCompletionHandler; typedef struct mal_IActivateAudioInterfaceAsyncOperation mal_IActivateAudioInterfaceAsyncOperation; #endif typedef struct mal_IPropertyStore mal_IPropertyStore; typedef struct mal_IAudioClient mal_IAudioClient; +typedef struct mal_IAudioClient2 mal_IAudioClient2; +typedef struct mal_IAudioClient3 mal_IAudioClient3; typedef struct mal_IAudioRenderClient mal_IAudioRenderClient; typedef struct mal_IAudioCaptureClient mal_IAudioCaptureClient; @@ -5057,6 +5386,7 @@ typedef mal_int64 MAL_REFERENCE_TIME; #define MAL_AUDCLNT_E_INVALID_DEVICE_PERIOD (-2004287456) #define MAL_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED (-2004287463) #define MAL_AUDCLNT_S_BUFFER_EMPTY (143196161) +#define MAL_AUDCLNT_E_DEVICE_IN_USE (-2004287478) typedef enum { @@ -5078,7 +5408,51 @@ typedef enum MAL_AUDCLNT_SHAREMODE_EXCLUSIVE } MAL_AUDCLNT_SHAREMODE; +typedef enum +{ + MAL_AudioCategory_Other = 0, // <-- mini_al is only caring about Other. +} MAL_AUDIO_STREAM_CATEGORY; + +typedef struct +{ + UINT32 cbSize; + BOOL bIsOffload; + MAL_AUDIO_STREAM_CATEGORY eCategory; +} mal_AudioClientProperties; + +// IUnknown +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IUnknown* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IUnknown* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IUnknown* pThis); +} mal_IUnknownVtbl; +struct mal_IUnknown +{ + mal_IUnknownVtbl* lpVtbl; +}; +HRESULT mal_IUnknown_QueryInterface(mal_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IUnknown_AddRef(mal_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IUnknown_Release(mal_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } + #ifdef MAL_WIN32_DESKTOP + // IMMNotificationClient + typedef struct + { + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IMMNotificationClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IMMNotificationClient* pThis); + + // IMMNotificationClient + HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState); + HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(mal_IMMNotificationClient* pThis, mal_EDataFlow dataFlow, mal_ERole role, LPCWSTR pDefaultDeviceID); + HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key); + } mal_IMMNotificationClientVtbl; + // IMMDeviceEnumerator typedef struct { @@ -5157,7 +5531,6 @@ typedef enum HRESULT mal_IMMDevice_GetId(mal_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); } HRESULT mal_IMMDevice_GetState(mal_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } #else - // IActivateAudioInterfaceAsyncOperation typedef struct { @@ -5167,7 +5540,7 @@ typedef enum ULONG (STDMETHODCALLTYPE * Release) (mal_IActivateAudioInterfaceAsyncOperation* pThis); // IActivateAudioInterfaceAsyncOperation - HRESULT (STDMETHODCALLTYPE * GetActivateResult)(mal_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, IUnknown** ppActivatedInterface); + HRESULT (STDMETHODCALLTYPE * GetActivateResult)(mal_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, mal_IUnknown** ppActivatedInterface); } mal_IActivateAudioInterfaceAsyncOperationVtbl; struct mal_IActivateAudioInterfaceAsyncOperation { @@ -5176,7 +5549,7 @@ typedef enum HRESULT mal_IActivateAudioInterfaceAsyncOperation_QueryInterface(mal_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } ULONG mal_IActivateAudioInterfaceAsyncOperation_AddRef(mal_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } ULONG mal_IActivateAudioInterfaceAsyncOperation_Release(mal_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } - HRESULT mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(mal_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } + HRESULT mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(mal_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, mal_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } #endif // IPropertyStore @@ -5250,6 +5623,115 @@ HRESULT mal_IAudioClient_Reset(mal_IAudioClient* pThis) HRESULT mal_IAudioClient_SetEventHandle(mal_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } HRESULT mal_IAudioClient_GetService(mal_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +// IAudioClient2 +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioClient2* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioClient2* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioClient2* pThis); + + // IAudioClient + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (mal_IAudioClient2* pThis, mal_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient2* pThis, mal_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (mal_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (mal_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (mal_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (mal_IAudioClient2* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (mal_IAudioClient2* pThis, const IID* const riid, void** pp); + + // IAudioClient2 + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (mal_IAudioClient2* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(mal_IAudioClient2* pThis, const mal_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(mal_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration); +} mal_IAudioClient2Vtbl; +struct mal_IAudioClient2 +{ + mal_IAudioClient2Vtbl* lpVtbl; +}; +HRESULT mal_IAudioClient2_QueryInterface(mal_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioClient2_AddRef(mal_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioClient2_Release(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioClient2_Initialize(mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +HRESULT mal_IAudioClient2_GetBufferSize(mal_IAudioClient2* pThis, mal_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +HRESULT mal_IAudioClient2_GetStreamLatency(mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +HRESULT mal_IAudioClient2_GetCurrentPadding(mal_IAudioClient2* pThis, mal_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +HRESULT mal_IAudioClient2_IsFormatSupported(mal_IAudioClient2* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +HRESULT mal_IAudioClient2_GetMixFormat(mal_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +HRESULT mal_IAudioClient2_GetDevicePeriod(mal_IAudioClient2* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +HRESULT mal_IAudioClient2_Start(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } +HRESULT mal_IAudioClient2_Stop(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IAudioClient2_Reset(mal_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } +HRESULT mal_IAudioClient2_SetEventHandle(mal_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +HRESULT mal_IAudioClient2_GetService(mal_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +HRESULT mal_IAudioClient2_IsOffloadCapable(mal_IAudioClient2* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +HRESULT mal_IAudioClient2_SetClientProperties(mal_IAudioClient2* pThis, const mal_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +HRESULT mal_IAudioClient2_GetBufferSizeLimits(mal_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } + + +// IAudioClient3 +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_IAudioClient3* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_IAudioClient3* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_IAudioClient3* pThis); + + // IAudioClient + HRESULT (STDMETHODCALLTYPE * Initialize) (mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (mal_IAudioClient3* pThis, mal_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(mal_IAudioClient3* pThis, mal_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (mal_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (mal_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (mal_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (mal_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (mal_IAudioClient3* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (mal_IAudioClient3* pThis, const IID* const riid, void** pp); + + // IAudioClient2 + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (mal_IAudioClient3* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(mal_IAudioClient3* pThis, const mal_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration); + + // IAudioClient3 + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(mal_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (mal_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); +} mal_IAudioClient3Vtbl; +struct mal_IAudioClient3 +{ + mal_IAudioClient3Vtbl* lpVtbl; +}; +HRESULT mal_IAudioClient3_QueryInterface(mal_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +ULONG mal_IAudioClient3_AddRef(mal_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } +ULONG mal_IAudioClient3_Release(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } +HRESULT mal_IAudioClient3_Initialize(mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MAL_REFERENCE_TIME bufferDuration, MAL_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +HRESULT mal_IAudioClient3_GetBufferSize(mal_IAudioClient3* pThis, mal_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +HRESULT mal_IAudioClient3_GetStreamLatency(mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +HRESULT mal_IAudioClient3_GetCurrentPadding(mal_IAudioClient3* pThis, mal_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +HRESULT mal_IAudioClient3_IsFormatSupported(mal_IAudioClient3* pThis, MAL_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +HRESULT mal_IAudioClient3_GetMixFormat(mal_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +HRESULT mal_IAudioClient3_GetDevicePeriod(mal_IAudioClient3* pThis, MAL_REFERENCE_TIME* pDefaultDevicePeriod, MAL_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +HRESULT mal_IAudioClient3_Start(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } +HRESULT mal_IAudioClient3_Stop(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } +HRESULT mal_IAudioClient3_Reset(mal_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } +HRESULT mal_IAudioClient3_SetEventHandle(mal_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +HRESULT mal_IAudioClient3_GetService(mal_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +HRESULT mal_IAudioClient3_IsOffloadCapable(mal_IAudioClient3* pThis, MAL_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +HRESULT mal_IAudioClient3_SetClientProperties(mal_IAudioClient3* pThis, const mal_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +HRESULT mal_IAudioClient3_GetBufferSizeLimits(mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MAL_REFERENCE_TIME* pMinBufferDuration, MAL_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +HRESULT mal_IAudioClient3_GetSharedModeEnginePeriod(mal_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +HRESULT mal_IAudioClient3_GetCurrentSharedModeEnginePeriod(mal_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +HRESULT mal_IAudioClient3_InitializeSharedAudioStream(mal_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } + // IAudioRenderClient typedef struct @@ -5298,60 +5780,226 @@ HRESULT mal_IAudioCaptureClient_GetBuffer(mal_IAudioCaptureClient* pThis, BYTE** HRESULT mal_IAudioCaptureClient_ReleaseBuffer(mal_IAudioCaptureClient* pThis, mal_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } HRESULT mal_IAudioCaptureClient_GetNextPacketSize(mal_IAudioCaptureClient* pThis, mal_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } -// This is the part that's preventing mini_al from being compiled as C with UWP. We need to implement IActivateAudioInterfaceCompletionHandler -// in C which is quite annoying. #ifndef MAL_WIN32_DESKTOP - #ifdef __cplusplus - #include - #include +#include +typedef struct mal_completion_handler_uwp mal_completion_handler_uwp; - class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler > - { - public: +typedef struct +{ + // IUnknown + HRESULT (STDMETHODCALLTYPE * QueryInterface)(mal_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (mal_completion_handler_uwp* pThis); + ULONG (STDMETHODCALLTYPE * Release) (mal_completion_handler_uwp* pThis); - malCompletionHandler() - : m_hEvent(NULL) - { - } + // IActivateAudioInterfaceCompletionHandler + HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(mal_completion_handler_uwp* pThis, mal_IActivateAudioInterfaceAsyncOperation* pActivateOperation); +} mal_completion_handler_uwp_vtbl; +struct mal_completion_handler_uwp +{ + mal_completion_handler_uwp_vtbl* lpVtbl; + mal_uint32 counter; + HANDLE hEvent; +}; - mal_result Init() - { - m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (m_hEvent == NULL) { - return MAL_ERROR; - } +HRESULT STDMETHODCALLTYPE mal_completion_handler_uwp_QueryInterface(mal_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) +{ + // We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To + // "implement" this, we just make sure we return pThis when the IAgileObject is requested. + if (!mal_is_guid_equal(riid, &MAL_IID_IUnknown) && !mal_is_guid_equal(riid, &MAL_IID_IActivateAudioInterfaceCompletionHandler) && !mal_is_guid_equal(riid, &MAL_IID_IAgileObject)) { + *ppObject = NULL; + return E_NOINTERFACE; + } - return MAL_SUCCESS; - } + // Getting here means the IID is IUnknown or IMMNotificationClient. + *ppObject = (void*)pThis; + ((mal_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} - void Uninit() - { - if (m_hEvent != NULL) { - CloseHandle(m_hEvent); - } - } +ULONG STDMETHODCALLTYPE mal_completion_handler_uwp_AddRef(mal_completion_handler_uwp* pThis) +{ + return (ULONG)mal_atomic_increment_32(&pThis->counter); +} - void Wait() - { - WaitForSingleObject(m_hEvent, INFINITE); - } +ULONG STDMETHODCALLTYPE mal_completion_handler_uwp_Release(mal_completion_handler_uwp* pThis) +{ + mal_uint32 newRefCount = mal_atomic_decrement_32(&pThis->counter); + if (newRefCount == 0) { + return 0; // We don't free anything here because we never allocate the object on the heap. + } - HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation) - { - (void)activateOperation; - SetEvent(m_hEvent); - return S_OK; - } + return (ULONG)newRefCount; +} - private: - HANDLE m_hEvent; // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted(). - }; - #else - #error "The UWP build is currently only supported in C++." - #endif +HRESULT STDMETHODCALLTYPE mal_completion_handler_uwp_ActivateCompleted(mal_completion_handler_uwp* pThis, mal_IActivateAudioInterfaceAsyncOperation* pActivateOperation) +{ + (void)pActivateOperation; + SetEvent(pThis->hEvent); + return S_OK; +} + + +static mal_completion_handler_uwp_vtbl g_malCompletionHandlerVtblInstance = { + mal_completion_handler_uwp_QueryInterface, + mal_completion_handler_uwp_AddRef, + mal_completion_handler_uwp_Release, + mal_completion_handler_uwp_ActivateCompleted +}; + +mal_result mal_completion_handler_uwp_init(mal_completion_handler_uwp* pHandler) +{ + mal_assert(pHandler != NULL); + mal_zero_object(pHandler); + + pHandler->lpVtbl = &g_malCompletionHandlerVtblInstance; + pHandler->counter = 1; + pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pHandler->hEvent == NULL) { + return MAL_ERROR; + } + + return MAL_SUCCESS; +} + +void mal_completion_handler_uwp_uninit(mal_completion_handler_uwp* pHandler) +{ + if (pHandler->hEvent != NULL) { + CloseHandle(pHandler->hEvent); + } +} + +void mal_completion_handler_uwp_wait(mal_completion_handler_uwp* pHandler) +{ + WaitForSingleObject(pHandler->hEvent, INFINITE); +} #endif // !MAL_WIN32_DESKTOP +// We need a virtual table for our notification client object that's used for detecting changes to the default device. +#ifdef MAL_WIN32_DESKTOP +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_QueryInterface(mal_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) +{ + // We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else + // we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. + if (!mal_is_guid_equal(riid, &MAL_IID_IUnknown) && !mal_is_guid_equal(riid, &MAL_IID_IMMNotificationClient)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + // Getting here means the IID is IUnknown or IMMNotificationClient. + *ppObject = (void*)pThis; + ((mal_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +ULONG STDMETHODCALLTYPE mal_IMMNotificationClient_AddRef(mal_IMMNotificationClient* pThis) +{ + return (ULONG)mal_atomic_increment_32(&pThis->counter); +} + +ULONG STDMETHODCALLTYPE mal_IMMNotificationClient_Release(mal_IMMNotificationClient* pThis) +{ + mal_uint32 newRefCount = mal_atomic_decrement_32(&pThis->counter); + if (newRefCount == 0) { + return 0; // We don't free anything here because we never allocate the object on the heap. + } + + return (ULONG)newRefCount; +} + + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDeviceStateChanged(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", pDeviceID, (unsigned int)dwNewState); +#endif + + (void)pThis; + (void)pDeviceID; + (void)dwNewState; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDeviceAdded(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", pDeviceID); +#endif + + // We don't need to worry about this event for our purposes. + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDeviceRemoved(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", pDeviceID); +#endif + + // We don't need to worry about this event for our purposes. + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDefaultDeviceChanged(mal_IMMNotificationClient* pThis, mal_EDataFlow dataFlow, mal_ERole role, LPCWSTR pDefaultDeviceID) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, pDefaultDeviceID); +#endif + + // We only ever use the eConsole role in mini_al. + if (role != mal_eConsole) { + return S_OK; + } + + // We only care about devices with the same data flow and role as the current device. + if ((pThis->pDevice->type == mal_device_type_playback && dataFlow != mal_eRender ) || + (pThis->pDevice->type == mal_device_type_capture && dataFlow != mal_eCapture)) { + return S_OK; + } + + // Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to + // AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in mini_al, we can try re-enabling this once + // it's fixed. + if (pThis->pDevice->exclusiveMode) { + return S_OK; + } + + // We don't change the device here - we change it in the worker thread to keep synchronization simple. To this I'm just setting a flag to + // indicate that the default device has changed. + mal_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultDeviceChanged, MAL_TRUE); + SetEvent(pThis->pDevice->wasapi.hBreakEvent); // <-- The main loop will be waiting on some events. We want to break from this wait ASAP so we can change the device as quickly as possible. + + + (void)pDefaultDeviceID; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnPropertyValueChanged(mal_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key) +{ +#ifdef MAL_DEBUG_OUTPUT + printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", pDeviceID); +#endif + + (void)pThis; + (void)pDeviceID; + (void)key; + return S_OK; +} + +static mal_IMMNotificationClientVtbl g_malNotificationCientVtbl = { + mal_IMMNotificationClient_QueryInterface, + mal_IMMNotificationClient_AddRef, + mal_IMMNotificationClient_Release, + mal_IMMNotificationClient_OnDeviceStateChanged, + mal_IMMNotificationClient_OnDeviceAdded, + mal_IMMNotificationClient_OnDeviceRemoved, + mal_IMMNotificationClient_OnDefaultDeviceChanged, + mal_IMMNotificationClient_OnPropertyValueChanged +}; +#endif // MAL_WIN32_DESKTOP mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { @@ -5377,13 +6025,13 @@ void mal_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, mal_device_i } #ifndef MAL_WIN32_DESKTOP -mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, IUnknown** ppActivatedInterface) +mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, mal_IUnknown** ppActivatedInterface) { mal_assert(pContext != NULL); mal_assert(ppAudioClient != NULL); mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - malCompletionHandler completionHandler; + mal_completion_handler_uwp completionHandler; IID iid; if (pDeviceID != NULL) { @@ -5397,20 +6045,28 @@ mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_d } LPOLESTR iidStr; +#if defined(__cplusplus) HRESULT hr = StringFromIID(iid, &iidStr); +#else + HRESULT hr = StringFromIID(&iid, &iidStr); +#endif if (FAILED(hr)) { return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY); } - mal_result result = completionHandler.Init(); + mal_result result = mal_completion_handler_uwp_init(&completionHandler); if (result != MAL_SUCCESS) { mal_CoTaskMemFree(pContext, iidStr); return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } +#if defined(__cplusplus) hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); +#else + hr = ActivateAudioInterfaceAsync(iidStr, &MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); +#endif if (FAILED(hr)) { - completionHandler.Uninit(); + mal_completion_handler_uwp_uninit(&completionHandler); mal_CoTaskMemFree(pContext, iidStr); return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } @@ -5418,11 +6074,11 @@ mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_d mal_CoTaskMemFree(pContext, iidStr); // Wait for the async operation for finish. - completionHandler.Wait(); - completionHandler.Uninit(); + mal_completion_handler_uwp_wait(&completionHandler); + mal_completion_handler_uwp_uninit(&completionHandler); HRESULT activateResult; - IUnknown* pActivatedInterface; + mal_IUnknown* pActivatedInterface; hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); @@ -5431,7 +6087,7 @@ mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_d } // Here is where we grab the IAudioClient interface. - hr = pActivatedInterface->QueryInterface(MAL_IID_IAudioClient, (void**)ppAudioClient); + hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)ppAudioClient); if (FAILED(hr)) { return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } @@ -5439,7 +6095,7 @@ mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_d if (ppActivatedInterface) { *ppActivatedInterface = pActivatedInterface; } else { - pActivatedInterface->Release(); + mal_IUnknown_Release(pActivatedInterface); } return MAL_SUCCESS; @@ -5781,6 +6437,13 @@ void mal_device_uninit__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); +#ifdef MAL_WIN32_DESKTOP + if (pDevice->wasapi.pDeviceEnumerator) { + ((mal_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((mal_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); + mal_IMMDeviceEnumerator_Release((mal_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); + } +#endif + if (pDevice->wasapi.pRenderClient) { mal_IAudioRenderClient_Release((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient); } @@ -5794,25 +6457,60 @@ void mal_device_uninit__wasapi(mal_device* pDevice) if (pDevice->wasapi.hEvent) { CloseHandle(pDevice->wasapi.hEvent); } - if (pDevice->wasapi.hStopEvent) { - CloseHandle(pDevice->wasapi.hStopEvent); + if (pDevice->wasapi.hBreakEvent) { + CloseHandle(pDevice->wasapi.hBreakEvent); } } -mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +typedef struct +{ + // Input. + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 sampleRateIn; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesIn; + mal_uint32 bufferSizeInMillisecondsIn; + mal_uint32 periodsIn; + mal_bool32 usingDefaultFormat; + mal_bool32 usingDefaultChannels; + mal_bool32 usingDefaultSampleRate; + mal_bool32 usingDefaultChannelMap; + mal_share_mode shareMode; + + // Output. + mal_IAudioClient* pAudioClient; + mal_IAudioRenderClient* pRenderClient; + mal_IAudioCaptureClient* pCaptureClient; + mal_format formatOut; + mal_uint32 channelsOut; + mal_uint32 sampleRateOut; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesOut; + mal_uint32 periodsOut; + mal_bool32 exclusiveMode; + char deviceName[256]; +} mal_device_init_internal_data__wasapi; + +mal_result mal_device_init_internal__wasapi(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, mal_device_init_internal_data__wasapi* pData) { (void)pContext; - mal_assert(pDevice != NULL); - mal_zero_object(&pDevice->wasapi); + mal_assert(pContext != NULL); + mal_assert(pData != NULL); + + pData->pAudioClient = NULL; + pData->pRenderClient = NULL; + pData->pCaptureClient = NULL; + HRESULT hr; mal_result result = MAL_SUCCESS; const char* errorMsg = ""; MAL_AUDCLNT_SHAREMODE shareMode = MAL_AUDCLNT_SHAREMODE_SHARED; - WAVEFORMATEXTENSIBLE* pBestFormatTemp = NULL; MAL_REFERENCE_TIME bufferDurationInMicroseconds; - + mal_bool32 wasInitializedUsingIAudioClient3 = MAL_FALSE; + WAVEFORMATEXTENSIBLE wf; #ifdef MAL_WIN32_DESKTOP mal_IMMDevice* pMMDevice = NULL; @@ -5821,40 +6519,39 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, goto done; } - hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient); + hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); if (FAILED(hr)) { errorMsg = "[WASAPI] Failed to activate device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto done; } #else - IUnknown* pActivatedInterface = NULL; - result = mal_context_get_IAudioClient_UWP__wasapi(pContext, type, pDeviceID, (mal_IAudioClient**)&pDevice->wasapi.pAudioClient, &pActivatedInterface); + mal_IUnknown* pActivatedInterface = NULL; + result = mal_context_get_IAudioClient_UWP__wasapi(pContext, type, pDeviceID, &pData->pAudioClient, &pActivatedInterface); if (result != MAL_SUCCESS) { goto done; } #endif - WAVEFORMATEXTENSIBLE wf; - mal_zero_object(&wf); - wf.Format.cbSize = sizeof(wf); - wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.Format.nChannels = (WORD)pDevice->channels; - wf.Format.nSamplesPerSec = (DWORD)pDevice->sampleRate; - wf.Format.wBitsPerSample = (WORD)mal_get_bytes_per_sample(pDevice->format)*8; - wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(pDevice->format == mal_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample; - wf.dwChannelMask = mal_channel_map_to_channel_mask__win32(pDevice->channelMap, pDevice->channels); - if (pDevice->format == mal_format_f32) { - wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; + // Try enabling hardware offloading. + mal_IAudioClient2* pAudioClient2; + hr = mal_IAudioClient_QueryInterface(pData->pAudioClient, &MAL_IID_IAudioClient2, (void**)&pAudioClient2); + if (SUCCEEDED(hr)) { + BOOL isHardwareOffloadingSupported = 0; + hr = mal_IAudioClient2_IsOffloadCapable(pAudioClient2, MAL_AudioCategory_Other, &isHardwareOffloadingSupported); + if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { + mal_AudioClientProperties clientProperties; + mal_zero_object(&clientProperties); + clientProperties.cbSize = sizeof(clientProperties); + clientProperties.bIsOffload = 1; + clientProperties.eCategory = MAL_AudioCategory_Other; + mal_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); + } } // Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. result = MAL_FORMAT_NOT_SUPPORTED; - if (pConfig->shareMode == mal_share_mode_exclusive) { + if (pData->shareMode == mal_share_mode_exclusive) { #ifdef MAL_WIN32_DESKTOP // In exclusive mode on desktop we always use the backend's native format. mal_IPropertyStore* pStore = NULL; @@ -5865,19 +6562,23 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, hr = mal_IPropertyStore_GetValue(pStore, &MAL_PKEY_AudioEngine_DeviceFormat, &prop); if (SUCCEEDED(hr)) { WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; - hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pDevice->wasapi.pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); + hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pData->pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); if (SUCCEEDED(hr)) { mal_copy_memory(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); } - mal_PropVariantClear(pDevice->pContext, &prop); + mal_PropVariantClear(pContext, &prop); } mal_IPropertyStore_Release(pStore); } #else - // With non-Desktop builds we just try using the requested format. - hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pDevice->wasapi.pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); + // I do not know how to query the device's native format on UWP so for now I'm just disabling support for + // exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() + // until you find one that works. + // + // TODO: Add support for exclusive mode to UWP. + hr = S_FALSE; #endif if (hr == S_OK) { @@ -5888,42 +6589,18 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, // Fall back to shared mode if necessary. if (result != MAL_SUCCESS) { + // In shared mode we are always using the format reported by the operating system. WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pDevice->wasapi.pAudioClient, (WAVEFORMATEX**)&pNativeFormat); - if (hr == S_OK) { - if (pDevice->usingDefaultFormat) { - wf.Format.wBitsPerSample = pNativeFormat->Format.wBitsPerSample; - wf.Format.nBlockAlign = pNativeFormat->Format.nBlockAlign; - wf.Format.nAvgBytesPerSec = pNativeFormat->Format.nAvgBytesPerSec; - wf.Samples.wValidBitsPerSample = pNativeFormat->Samples.wValidBitsPerSample; - wf.SubFormat = pNativeFormat->SubFormat; - } - if (pDevice->usingDefaultChannels) { - wf.Format.nChannels = pNativeFormat->Format.nChannels; - } - if (pDevice->usingDefaultSampleRate) { - wf.Format.nSamplesPerSec = pNativeFormat->Format.nSamplesPerSec; - } - if (pDevice->usingDefaultChannelMap) { - wf.dwChannelMask = pNativeFormat->dwChannelMask; - } - - mal_CoTaskMemFree(pDevice->pContext, pNativeFormat); - pNativeFormat = NULL; - } - - hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pDevice->wasapi.pAudioClient, MAL_AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); - if (hr != S_OK && hr != S_FALSE) { - hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pDevice->wasapi.pAudioClient, (WAVEFORMATEX**)&pBestFormatTemp); - if (hr != S_OK) { - result = MAL_FORMAT_NOT_SUPPORTED; - } else { - result = MAL_SUCCESS; - } + hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat); + if (hr != S_OK) { + result = MAL_FORMAT_NOT_SUPPORTED; } else { + mal_copy_memory(&wf, pNativeFormat, sizeof(wf)); result = MAL_SUCCESS; } + mal_CoTaskMemFree(pContext, pNativeFormat); + shareMode = MAL_AUDCLNT_SHAREMODE_SHARED; } @@ -5933,47 +6610,22 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, goto done; } - if (pBestFormatTemp != NULL) { - mal_copy_memory(&wf, pBestFormatTemp, sizeof(wf)); - mal_CoTaskMemFree(pDevice->pContext, pBestFormatTemp); - } - - - pDevice->internalFormat = mal_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); - pDevice->internalChannels = wf.Format.nChannels; - pDevice->internalSampleRate = wf.Format.nSamplesPerSec; + pData->formatOut = mal_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); + pData->channelsOut = wf.Format.nChannels; + pData->sampleRateOut = wf.Format.nSamplesPerSec; // Get the internal channel map based on the channel mask. - mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); // If we're using a default buffer size we need to calculate it based on the efficiency of the system. - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // We need a slightly bigger buffer if we're using shared mode to cover the inherent tax association with shared mode. - float fShareMode; - if (pConfig->shareMode == mal_share_mode_shared) { - fShareMode = 1.0f; - } else { - fShareMode = 0.8f; - } - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; - } - - // Backend tax. Need to fiddle with this. - float fBackend = 1.0; - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fShareMode*fType*fBackend); + pData->periodsOut = pData->periodsIn; + pData->bufferSizeInFramesOut = pData->bufferSizeInFramesIn; + if (pData->bufferSizeInFramesOut == 0) { + pData->bufferSizeInFramesOut = mal_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut); } - bufferDurationInMicroseconds = ((mal_uint64)pDevice->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate; + bufferDurationInMicroseconds = ((mal_uint64)pData->bufferSizeInFramesOut * 1000 * 1000) / pData->sampleRateOut; + // Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. if (shareMode == MAL_AUDCLNT_SHAREMODE_EXCLUSIVE) { @@ -5984,7 +6636,7 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, // it and trying it again. hr = E_FAIL; for (;;) { - hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pData->pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); if (hr == MAL_AUDCLNT_E_INVALID_DEVICE_PERIOD) { if (bufferDuration > 500*10000) { break; @@ -6003,21 +6655,21 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, if (hr == MAL_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { UINT bufferSizeInFrames; - hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &bufferSizeInFrames); + hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (SUCCEEDED(hr)) { bufferDuration = (MAL_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); // Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! - mal_IAudioClient_Release((mal_IAudioClient*)pDevice->wasapi.pAudioClient); + mal_IAudioClient_Release((mal_IAudioClient*)pData->pAudioClient); #ifdef MAL_WIN32_DESKTOP - hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient); + hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); #else - hr = pActivatedInterface->QueryInterface(MAL_IID_IAudioClient, &pDevice->wasapi.pAudioClient); + hr = mal_IUnknown_QueryInterface(pActivatedInterface, &MAL_IID_IAudioClient, (void**)&pData->pAudioClient); #endif if (SUCCEEDED(hr)) { - hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pData->pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); } } } @@ -6025,37 +6677,72 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, if (FAILED(hr)) { // Failed to initialize in exclusive mode. We don't return an error here, but instead fall back to shared mode. shareMode = MAL_AUDCLNT_SHAREMODE_SHARED; - - //errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; - //goto done; } } if (shareMode == MAL_AUDCLNT_SHAREMODE_SHARED) { // Shared. - MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; - hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + + // Low latency shared mode via IAudioClient3. + mal_IAudioClient3* pAudioClient3 = NULL; + hr = mal_IAudioClient_QueryInterface(pData->pAudioClient, &MAL_IID_IAudioClient3, (void**)&pAudioClient3); + if (SUCCEEDED(hr)) { + UINT32 defaultPeriodInFrames; + UINT32 fundamentalPeriodInFrames; + UINT32 minPeriodInFrames; + UINT32 maxPeriodInFrames; + hr = mal_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + if (SUCCEEDED(hr)) { + UINT32 desiredPeriodInFrames = pData->bufferSizeInFramesOut / pData->periodsOut; + + // Make sure the period size is a multiple of fundamentalPeriodInFrames. + desiredPeriodInFrames = desiredPeriodInFrames / fundamentalPeriodInFrames; + desiredPeriodInFrames = desiredPeriodInFrames * fundamentalPeriodInFrames; + + // The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. + desiredPeriodInFrames = mal_clamp(desiredPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); + + hr = mal_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, desiredPeriodInFrames, (WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + wasInitializedUsingIAudioClient3 = MAL_TRUE; + pData->bufferSizeInFramesOut = desiredPeriodInFrames * pData->periodsOut; + } } + mal_IAudioClient3_Release(pAudioClient3); + pAudioClient3 = NULL; + } + + // If we don't have an IAudioClient3 then we need to use the normal initialization routine. + if (!wasInitializedUsingIAudioClient3) { + MAL_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; + hr = mal_IAudioClient_Initialize((mal_IAudioClient*)pData->pAudioClient, shareMode, MAL_AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); + if (FAILED(hr)) { + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED; + } else if (hr == MAL_AUDCLNT_E_DEVICE_IN_USE) { + errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MAL_DEVICE_BUSY; + } else { + errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + goto done; + } + } + } + + if (!wasInitializedUsingIAudioClient3) { + hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pData->pAudioClient, &pData->bufferSizeInFramesOut); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; goto done; } } - hr = mal_IAudioClient_GetBufferSize((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &pDevice->bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; - goto done; - } - if (type == mal_device_type_playback) { - hr = mal_IAudioClient_GetService((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &MAL_IID_IAudioRenderClient, &pDevice->wasapi.pRenderClient); + hr = mal_IAudioClient_GetService((mal_IAudioClient*)pData->pAudioClient, &MAL_IID_IAudioRenderClient, (void**)&pData->pRenderClient); } else { - hr = mal_IAudioClient_GetService((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &MAL_IID_IAudioCaptureClient, &pDevice->wasapi.pCaptureClient); + hr = mal_IAudioClient_GetService((mal_IAudioClient*)pData->pAudioClient, &MAL_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient); } if (FAILED(hr)) { @@ -6065,9 +6752,9 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, if (shareMode == MAL_AUDCLNT_SHAREMODE_SHARED) { - pDevice->exclusiveMode = MAL_FALSE; + pData->exclusiveMode = MAL_FALSE; } else /*if (shareMode == MAL_AUDCLNT_SHAREMODE_EXCLUSIVE)*/ { - pDevice->exclusiveMode = MAL_TRUE; + pData->exclusiveMode = MAL_TRUE; } @@ -6080,7 +6767,7 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, mal_PropVariantInit(&varName); hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &varName); if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pDevice->name, sizeof(pDevice->name), 0, FALSE); + WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); mal_PropVariantClear(pContext, &varName); } @@ -6088,6 +6775,157 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, } #endif +done: + // Clean up. +#ifdef MAL_WIN32_DESKTOP + if (pMMDevice != NULL) { + mal_IMMDevice_Release(pMMDevice); + } +#else + if (pActivatedInterface != NULL) { + mal_IUnknown_Release(pActivatedInterface); + } +#endif + + if (result != MAL_SUCCESS) { + if (pData->pRenderClient) { + mal_IAudioRenderClient_Release((mal_IAudioRenderClient*)pData->pRenderClient); + pData->pRenderClient = NULL; + } + if (pData->pCaptureClient) { + mal_IAudioCaptureClient_Release((mal_IAudioCaptureClient*)pData->pCaptureClient); + pData->pCaptureClient = NULL; + } + if (pData->pAudioClient) { + mal_IAudioClient_Release((mal_IAudioClient*)pData->pAudioClient); + pData->pAudioClient = NULL; + } + + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, errorMsg, result); + } else { + return MAL_SUCCESS; + } +} + +mal_result mal_device_reinit__wasapi(mal_device* pDevice) +{ + mal_device_init_internal_data__wasapi data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + mal_result result = mal_device_init_internal__wasapi(pDevice->pContext, pDevice->type, NULL, &data); + if (result != MAL_SUCCESS) { + return result; + } + + // At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. + if (pDevice->wasapi.pRenderClient) { + mal_IAudioRenderClient_Release((mal_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + pDevice->wasapi.pRenderClient = NULL; + } + if (pDevice->wasapi.pCaptureClient) { + mal_IAudioCaptureClient_Release((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClient) { + mal_IAudioClient_Release((mal_IAudioClient*)pDevice->wasapi.pAudioClient); + pDevice->wasapi.pAudioClient = NULL; + } + + pDevice->wasapi.pAudioClient = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = data.exclusiveMode; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + + mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent); + + return MAL_SUCCESS; +} + +mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + (void)pConfig; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->wasapi); + + mal_result result = MAL_SUCCESS; + const char* errorMsg = ""; + + mal_device_init_internal_data__wasapi data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + result = mal_device_init_internal__wasapi(pDevice->pContext, type, pDeviceID, &data); + if (result != MAL_SUCCESS) { + return result; + } + + pDevice->wasapi.pAudioClient = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = data.exclusiveMode; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + + + + // We need to get notifications of when the default device changes. We do this through a device enumerator by + // registering a IMMNotificationClient with it. We only care about this if it's the default device. +#ifdef MAL_WIN32_DESKTOP + mal_IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to create device enumerator.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + goto done; + } + + pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_malNotificationCientVtbl; + pDevice->wasapi.notificationClient.counter = 1; + pDevice->wasapi.notificationClient.pDevice = pDevice; + + hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); + if (SUCCEEDED(hr)) { + pDevice->wasapi.pDeviceEnumerator = (mal_ptr)pDeviceEnumerator; + } else { + // Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. + mal_IMMDeviceEnumerator_Release(pDeviceEnumerator); + } +#endif + // We need to create and set the event for event-driven mode. This event is signalled whenever a new chunk of audio // data needs to be written or read from the device. @@ -6102,9 +6940,9 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, // When the device is playing the worker thread will be waiting on a bunch of notification events. To return from // this wait state we need to signal a special event. - pDevice->wasapi.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (pDevice->wasapi.hStopEvent == NULL) { - errorMsg = "[WASAPI] Failed to create stop event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT; + pDevice->wasapi.hBreakEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pDevice->wasapi.hBreakEvent == NULL) { + errorMsg = "[WASAPI] Failed to create break event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT; goto done; } @@ -6112,16 +6950,6 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, done: // Clean up. -#ifdef MAL_WIN32_DESKTOP - if (pMMDevice != NULL) { - mal_IMMDevice_Release(pMMDevice); - } -#else - if (pActivatedInterface != NULL) { - pActivatedInterface->Release(); - } -#endif - if (result != MAL_SUCCESS) { mal_device_uninit__wasapi(pDevice); return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, errorMsg, result); @@ -6162,6 +6990,10 @@ mal_result mal_device__stop_backend__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); + if (pDevice->wasapi.pAudioClient == NULL) { + return MAL_DEVICE_NOT_INITIALIZED; + } + HRESULT hr = mal_IAudioClient_Stop((mal_IAudioClient*)pDevice->wasapi.pAudioClient); if (FAILED(hr)) { return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); @@ -6183,60 +7015,48 @@ mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice) // The main loop will be waiting on a bunch of events via the WaitForMultipleObjects() API. One of those events // is a special event we use for forcing that function to return. pDevice->wasapi.breakFromMainLoop = MAL_TRUE; - SetEvent(pDevice->wasapi.hStopEvent); + SetEvent(pDevice->wasapi.hBreakEvent); return MAL_SUCCESS; } -mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice) +mal_result mal_device__get_available_frames__wasapi(mal_device* pDevice, mal_uint32* pFrameCount) { mal_assert(pDevice != NULL); + mal_assert(pFrameCount != NULL); + + *pFrameCount = 0; -#if 1 - if (pDevice->type == mal_device_type_playback) { - mal_uint32 paddingFramesCount; - HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return 0; - } - - if (pDevice->exclusiveMode) { - return paddingFramesCount; - } else { - return pDevice->bufferSizeInFrames - paddingFramesCount; - } - } else { - mal_uint32 framesAvailable; - HRESULT hr = mal_IAudioCaptureClient_GetNextPacketSize((mal_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, &framesAvailable); - if (FAILED(hr)) { - return 0; - } - - return framesAvailable; - } -#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; + return MAL_DEVICE_UNAVAILABLE; } + // Slightly different rules for exclusive and shared modes. if (pDevice->exclusiveMode) { - return paddingFramesCount; + *pFrameCount = paddingFramesCount; } else { - return pDevice->bufferSizeInFrames - paddingFramesCount; + if (pDevice->type == mal_device_type_playback) { + *pFrameCount = pDevice->bufferSizeInFrames - paddingFramesCount; + } else { + *pFrameCount = paddingFramesCount; + } } -#endif + + return MAL_SUCCESS; } -mal_uint32 mal_device__wait_for_frames__wasapi(mal_device* pDevice) +mal_result mal_device__wait_for_frames__wasapi(mal_device* pDevice, mal_uint32* pFrameCount) { mal_assert(pDevice != NULL); + mal_result result; + while (!pDevice->wasapi.breakFromMainLoop) { // Wait for a buffer to become available or for the stop event to be signalled. HANDLE hEvents[2]; hEvents[0] = (HANDLE)pDevice->wasapi.hEvent; - hEvents[1] = (HANDLE)pDevice->wasapi.hStopEvent; + hEvents[1] = (HANDLE)pDevice->wasapi.hBreakEvent; if (WaitForMultipleObjects(mal_countof(hEvents), hEvents, FALSE, INFINITE) == WAIT_FAILED) { break; } @@ -6247,33 +7067,89 @@ mal_uint32 mal_device__wait_for_frames__wasapi(mal_device* pDevice) break; } - mal_uint32 framesAvailable = mal_device__get_available_frames__wasapi(pDevice); - if (framesAvailable > 0) { - return framesAvailable; + // Make sure we break from the main loop if requested from an external factor. + if (pDevice->wasapi.breakFromMainLoop) { + break; + } + + // We may want to reinitialize the device. Only do this if this device is the default. + mal_bool32 needDeviceReinit = MAL_FALSE; + + mal_bool32 hasDefaultDeviceChanged = pDevice->wasapi.hasDefaultDeviceChanged; + if (hasDefaultDeviceChanged && pDevice->isDefaultDevice) { + needDeviceReinit = MAL_TRUE; + } + + if (!needDeviceReinit) { + result = mal_device__get_available_frames__wasapi(pDevice, pFrameCount); + if (result != MAL_SUCCESS) { + if (!pDevice->exclusiveMode) { + needDeviceReinit = MAL_TRUE; + } else { + return result; + } + } + } + + + mal_atomic_exchange_32(&pDevice->wasapi.hasDefaultDeviceChanged, MAL_FALSE); + + // Here is where the device is re-initialized if required. + if (needDeviceReinit) { + #ifdef MAL_DEBUG_OUTPUT + printf("=== CHANGING DEVICE ===\n"); + #endif + + if (pDevice->pContext->onDeviceReinit) { + mal_result reinitResult = pDevice->pContext->onDeviceReinit(pDevice); + if (reinitResult != MAL_SUCCESS) { + return reinitResult; + } + + mal_device__post_init_setup(pDevice); + + // Start playing the device again, and then continue the loop from the top. + if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { + if (pDevice->pContext->onDeviceStart) { + pDevice->pContext->onDeviceStart(pDevice); + } + continue; + } + } + } + + + if (*pFrameCount > 0) { + return MAL_SUCCESS; } } // We'll get here if the loop was terminated. Just return whatever's available. - return mal_device__get_available_frames__wasapi(pDevice); + return mal_device__get_available_frames__wasapi(pDevice, pFrameCount); } mal_result mal_device__main_loop__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); - // Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects(). - ResetEvent(pDevice->wasapi.hStopEvent); + // Make sure the break event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects(). + ResetEvent(pDevice->wasapi.hBreakEvent); pDevice->wasapi.breakFromMainLoop = MAL_FALSE; while (!pDevice->wasapi.breakFromMainLoop) { - mal_uint32 framesAvailable = mal_device__wait_for_frames__wasapi(pDevice); + mal_uint32 framesAvailable; + mal_result result = mal_device__wait_for_frames__wasapi(pDevice, &framesAvailable); + if (result != MAL_SUCCESS) { + return result; + } + if (framesAvailable == 0) { continue; } // If it's a playback device, don't bother grabbing more data if the device is being stopped. if (pDevice->wasapi.breakFromMainLoop && pDevice->type == mal_device_type_playback) { - return MAL_FALSE; + return MAL_SUCCESS; } if (pDevice->type == mal_device_type_playback) { @@ -6365,6 +7241,7 @@ mal_result mal_context_init__wasapi(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__wasapi; pContext->onDeviceInit = mal_device_init__wasapi; pContext->onDeviceUninit = mal_device_uninit__wasapi; + pContext->onDeviceReinit = mal_device_reinit__wasapi; pContext->onDeviceStart = mal_device__start_backend__wasapi; pContext->onDeviceStop = mal_device__stop_backend__wasapi; pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__wasapi; @@ -7230,25 +8107,6 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, return MAL_FORMAT_NOT_SUPPORTED; } - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; - } - - // Backend tax. Need to fiddle with this. - float fBackend = 3.0; - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); - } - - WAVEFORMATEXTENSIBLE wf; mal_zero_object(&wf); wf.Format.cbSize = sizeof(wf); @@ -7348,6 +8206,11 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); } + // We need to wait until we know the sample rate before we can calculate the buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); @@ -7405,6 +8268,10 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, wf.Format.nSamplesPerSec); + } + bufferSizeInBytes = pDevice->bufferSizeInFrames * wf.Format.nChannels * mal_get_bytes_per_sample(pDevice->format); MAL_DSCBUFFERDESC descDS; @@ -8135,6 +9002,8 @@ void mal_device_uninit__winmm(mal_device* pDevice) mal_free(pDevice->winmm._pHeapData); CloseHandle((HANDLE)pDevice->winmm.hEvent); + + mal_zero_object(&pDevice->winmm); // Safety. } mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) @@ -8276,23 +9145,12 @@ mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, c mal_get_standard_channel_map(mal_standard_channel_map_microsoft, pDevice->internalChannels, pDevice->internalChannelMap); - // Latency with WinMM seems pretty bad from my testing... Need to increase the default buffer size. - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 4; // <-- Latency with WinMM seems pretty bad from my testing... + pDevice->bufferSizeInFrames = mal_scale_buffer_size(pDevice->bufferSizeInFrames, bufferSizeScaleFactor); } - - // Backend tax. Need to fiddle with this. - float fBackend = 10.0; - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); } // The size of the intermediary buffer needs to be able to fit every fragment. @@ -8330,6 +9188,10 @@ mal_result mal_device__start_backend__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); + if (pDevice->winmm.hDevice == NULL) { + return MAL_INVALID_ARGS; + } + if (pDevice->type == mal_device_type_playback) { // Playback. The device is started when we call waveOutWrite() with a block of data. From MSDN: // @@ -8394,6 +9256,10 @@ mal_result mal_device__stop_backend__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); + if (pDevice->winmm.hDevice == NULL) { + return MAL_INVALID_ARGS; + } + if (pDevice->type == mal_device_type_playback) { MMRESULT resultMM = ((MAL_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevice); if (resultMM != MMSYSERR_NOERROR) { @@ -8468,7 +9334,7 @@ mal_result mal_device__main_loop__winmm(mal_device* pDevice) MMRESULT resultMM = ((MAL_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to unprepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); - break; + return MAL_DEVICE_UNAVAILABLE; } mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); @@ -8482,7 +9348,7 @@ mal_result mal_device__main_loop__winmm(mal_device* pDevice) resultMM = ((MAL_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to prepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); - break; + return MAL_DEVICE_UNAVAILABLE; } } else { // Capture. @@ -8494,7 +9360,7 @@ mal_result mal_device__main_loop__winmm(mal_device* pDevice) MMRESULT resultMM = ((MAL_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to unprepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); - break; + return MAL_DEVICE_UNAVAILABLE; } mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); @@ -8507,7 +9373,7 @@ mal_result mal_device__main_loop__winmm(mal_device* pDevice) resultMM = ((MAL_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); - break; + return MAL_DEVICE_UNAVAILABLE; } } @@ -8527,14 +9393,14 @@ mal_result mal_device__main_loop__winmm(mal_device* pDevice) MMRESULT resultMM = ((MAL_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to write data to the internal playback device.", mal_result_from_MMRESULT(resultMM)); - break; + return MAL_DEVICE_UNAVAILABLE; } } else { // Capture. MMRESULT resultMM = ((MAL_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); - break; + return MAL_DEVICE_UNAVAILABLE; } } } @@ -8843,16 +9709,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", @@ -8879,8 +9735,8 @@ static struct const char* name; float scale; } g_malDefaultBufferSizeScalesALSA[] = { - {"bcm2835 IEC958/HDMI", 6.0f}, - {"bcm2835 ALSA", 6.0f} + {"bcm2835 IEC958/HDMI", 2.0f}, + {"bcm2835 ALSA", 2.0f} }; float mal_find_default_buffer_size_scale__alsa(const char* deviceName) @@ -8900,20 +9756,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; } } @@ -9528,22 +10416,9 @@ mal_uint32 mal_device__wait_for_frames__alsa(mal_device* pDevice, mal_bool32* pR if (pRequiresRestart) *pRequiresRestart = MAL_FALSE; + // I want it so that this function returns the period size in frames. We just wait until that number of frames are available and then return. + mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; while (!pDevice->alsa.breakFromMainLoop) { - int waitResult = ((mal_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((mal_snd_pcm_t*)pDevice->alsa.pPCM, -1); - if (waitResult < 0) { - if (waitResult == -EPIPE) { - if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MAL_TRUE) < 0) { - return 0; - } - - if (pRequiresRestart) *pRequiresRestart = MAL_TRUE; // A device recovery means a restart for mmap mode. - } - } - - if (pDevice->alsa.breakFromMainLoop) { - return 0; - } - mal_snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((mal_snd_pcm_t*)pDevice->alsa.pPCM); if (framesAvailable < 0) { if (framesAvailable == -EPIPE) { @@ -9551,7 +10426,10 @@ mal_uint32 mal_device__wait_for_frames__alsa(mal_device* pDevice, mal_bool32* pR return 0; } - if (pRequiresRestart) *pRequiresRestart = MAL_TRUE; // A device recovery means a restart for mmap mode. + // A device recovery means a restart for mmap mode. + if (pRequiresRestart) { + *pRequiresRestart = MAL_TRUE; + } // Try again, but if it fails this time just return an error. framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((mal_snd_pcm_t*)pDevice->alsa.pPCM); @@ -9561,21 +10439,26 @@ mal_uint32 mal_device__wait_for_frames__alsa(mal_device* pDevice, mal_bool32* pR } } - // Ideally I'd like to keep the number of frames consistent with the period size, but unfortunately it appears - // this does not work correctly in some situations. In my testing, this breaks when the period size is <= 1024 - // when using "hw:0,0" in a VirtualBox guest. What's happening is that it looks like snd_pcm_writei() (and - // snd_pcm_mmap_commit() in MMAP mode) are not physically writing the data to the internal buffers. As a result, - // snd_pcm_wait() is returning immediately, always reporting the full buffer size as available. I'm not sure if - // this is me not doing something right, or if it's some kind of driver bug, but to fix this we just need to - // report the exact value returned by snd_pcm_avail_update() and not clamp it to the period size. -#if 1 - return framesAvailable; -#else - mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; if (framesAvailable >= periodSizeInFrames) { return periodSizeInFrames; } -#endif + + if (framesAvailable < periodSizeInFrames) { + // Less than a whole period is available so keep waiting. + int waitResult = ((mal_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((mal_snd_pcm_t*)pDevice->alsa.pPCM, -1); + if (waitResult < 0) { + if (waitResult == -EPIPE) { + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((mal_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MAL_TRUE) < 0) { + return 0; + } + + // A device recovery means a restart for mmap mode. + if (pRequiresRestart) { + *pRequiresRestart = MAL_TRUE; + } + } + } + } } // We'll get here if the loop was terminated. Just return whatever's available. @@ -9813,32 +10696,15 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co return result; } - // If using the default buffer size we do some calculations based on a simple profiling test. + // We may be scaling the size of the buffer. + float bufferSizeScaleFactor = 1; + + // If using the default buffer size we may want to apply some device-specific scaling for known devices that have peculiar latency characteristics (looking at you Raspberry Pi!). if (pDevice->usingDefaultBufferSize) { mal_snd_pcm_info_t* pInfo = (mal_snd_pcm_info_t*)alloca(((mal_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)()); mal_zero_memory(pInfo, ((mal_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)()); - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // We need a slightly bigger buffer if we're using shared mode to cover the inherent tax association with shared mode. - float fShareMode; - if (pConfig->shareMode == mal_share_mode_shared) { - fShareMode = 1.0f; - } else { - fShareMode = 0.8f; - } - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; - } - // We may need to scale the size of the buffer depending on the device. - float fDevice = 1; if (((mal_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) { const char* deviceName = ((mal_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo); if (deviceName != NULL) { @@ -9859,7 +10725,7 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co if ((type == mal_device_type_playback && (IOID == NULL || mal_strcmp(IOID, "Output") == 0)) || (type == mal_device_type_capture && (IOID != NULL && mal_strcmp(IOID, "Input" ) == 0))) { if (mal_strcmp(NAME, deviceName) == 0) { - fDevice = mal_find_default_buffer_size_scale__alsa(DESC); + bufferSizeScaleFactor = mal_find_default_buffer_size_scale__alsa(DESC); foundDevice = MAL_TRUE; } } @@ -9876,17 +10742,13 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); } else { - fDevice = mal_find_default_buffer_size_scale__alsa(deviceName); + bufferSizeScaleFactor = mal_find_default_buffer_size_scale__alsa(deviceName); } } - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fShareMode*fType*fDevice); } } - - // Hardware parameters. mal_snd_pcm_hw_params_t* pHWParams = (mal_snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); @@ -9901,7 +10763,7 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co // // Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. pDevice->alsa.isUsingMMap = MAL_FALSE; - if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it so I can test it... Contributions welcome. + if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, MAL_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { pDevice->alsa.isUsingMMap = MAL_TRUE; } @@ -9930,14 +10792,22 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co if (!((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) { // The requested format is not supported so now try running through the list of formats and return the best one. mal_snd_pcm_format_t preferredFormatsALSA[] = { + MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16 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_S16_BE; + preferredFormatsALSA[1] = MAL_SND_PCM_FORMAT_FLOAT_BE; + preferredFormatsALSA[2] = MAL_SND_PCM_FORMAT_S32_BE; + preferredFormatsALSA[3] = MAL_SND_PCM_FORMAT_S24_3BE; + 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])) { @@ -9998,14 +10868,10 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co pDevice->internalSampleRate = (mal_uint32)sampleRate; - // Periods. - mal_uint32 periods = pConfig->periods; - int dir = 0; - if (((mal_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &periods, &dir) < 0) { - mal_device_uninit__alsa(pDevice); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + // At this point we know the internal sample rate which means we can calculate the buffer size in frames. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_scale_buffer_size(mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate), bufferSizeScaleFactor); } - pDevice->periods = periods; // Buffer Size mal_snd_pcm_uframes_t actualBufferSize = pDevice->bufferSizeInFrames; @@ -10015,6 +10881,14 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co } pDevice->bufferSizeInFrames = actualBufferSize; + // Periods. + mal_uint32 periods = pConfig->periods; + if (((mal_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &periods, NULL) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + pDevice->periods = periods; + // Apply hardware parameters. if (((mal_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)((mal_snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { @@ -10943,20 +11817,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 +11845,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; } } @@ -11701,23 +12589,20 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c } #endif - // If using the default buffer size try to find an appropriate default. - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); + // Buffer size. + bufferSizeInFrames = pDevice->bufferSizeInFrames; + if (bufferSizeInFrames == 0) { + bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, ss.rate); - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; + // PulseAudio seems to need a bit of an bit of size to the buffer to be reliable. + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 1.0f; + if (type == mal_device_type_capture) { + bufferSizeScaleFactor = 2.0f; + } + + bufferSizeInFrames = mal_scale_buffer_size(bufferSizeInFrames, bufferSizeScaleFactor); } - - // Backend tax. Need to fiddle with this. - float fBackend = 1.2; - - bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); } attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels; @@ -12757,6 +13642,7 @@ typedef Boolean (* mal_CFStringGetCString_proc)(CFStringRef theString, char* buf typedef OSStatus (* mal_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); typedef OSStatus (* mal_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); typedef OSStatus (* mal_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); +typedef OSStatus (* mal_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); #endif // AudioToolbox @@ -12775,6 +13661,8 @@ typedef OSStatus (* mal_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderA #define MAL_COREAUDIO_OUTPUT_BUS 0 #define MAL_COREAUDIO_INPUT_BUS 1 +mal_result mal_device_reinit_internal__coreaudio(mal_device* pDevice, mal_bool32 disposePreviousAudioUnit); + // Core Audio // @@ -12967,9 +13855,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; } @@ -13588,21 +14476,17 @@ mal_result mal_find_AudioObjectID(mal_context* pContext, mal_device_type type, c } -mal_result mal_device_find_best_format__coreaudio(const mal_device* pDevice, AudioStreamBasicDescription* pFormat) +mal_result mal_find_best_format__coreaudio(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_bool32 usingDefaultFormat, mal_bool32 usingDefaultChannels, mal_bool32 usingDefaultSampleRate, AudioStreamBasicDescription* pFormat) { - mal_assert(pDevice != NULL); - - AudioObjectID deviceObjectID = (AudioObjectID)pDevice->coreaudio.deviceObjectID; - UInt32 deviceFormatDescriptionCount; AudioStreamRangedDescription* pDeviceFormatDescriptions; - mal_result result = mal_get_AudioObject_stream_descriptions(pDevice->pContext, deviceObjectID, pDevice->type, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); + mal_result result = mal_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); if (result != MAL_SUCCESS) { return result; } - mal_uint32 desiredSampleRate = pDevice->sampleRate; - if (pDevice->usingDefaultSampleRate) { + mal_uint32 desiredSampleRate = sampleRate; + if (usingDefaultSampleRate) { // When using the device's default sample rate, we get the highest priority standard rate supported by the device. Otherwise // we just use the pre-set rate. for (mal_uint32 iStandardRate = 0; iStandardRate < mal_countof(g_malStandardSampleRatePriorities); ++iStandardRate) { @@ -13625,13 +14509,13 @@ mal_result mal_device_find_best_format__coreaudio(const mal_device* pDevice, Aud } } - mal_uint32 desiredChannelCount = pDevice->channels; - if (pDevice->usingDefaultChannels) { - mal_get_AudioObject_channel_count(pDevice->pContext, deviceObjectID, pDevice->type, &desiredChannelCount); // <-- Not critical if this fails. + mal_uint32 desiredChannelCount = channels; + if (usingDefaultChannels) { + mal_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &desiredChannelCount); // <-- Not critical if this fails. } - mal_format desiredFormat = pDevice->format; - if (pDevice->usingDefaultFormat) { + mal_format desiredFormat = format; + if (usingDefaultFormat) { desiredFormat = g_malFormatPriorities[0]; } @@ -14100,6 +14984,30 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro } if (!isRunning) { + // The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: + // + // 1) When the device is unplugged, this will be called _before_ the default device change notification. + // 2) When the device is changed via the default device change notification, this will be called _after_ the switch. + // + // For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. + if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { + // It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device + // via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the + // device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it + // hasn't!). + if (pDevice->coreaudio.isSwitchingDevice) { + return; + } + + // Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio + // will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most + // likely be successful in switching to the new device. + // + // TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted. + return; + } + + // Getting here means we need to stop the device. mal_stop_proc onStop = pDevice->onStop; if (onStop) { onStop(pDevice); @@ -14107,14 +15015,92 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro } } +#if defined(MAL_APPLE_DESKTOP) +OSStatus mal_default_output_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) +{ + (void)objectID; -mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + if (pDevice->isDefaultDevice) { + // Not sure if I really need to check this, but it makes me feel better. + if (addressCount == 0) { + return noErr; + } + + if ((pDevice->type == mal_device_type_playback && pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) || + (pDevice->type == mal_device_type_capture && pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice)) { +#ifdef MAL_DEBUG_OUTPUT + printf("Device Changed: addressCount=%d, pAddresses[0].mElement=%d\n", addressCount, pAddresses[0].mElement); +#endif + pDevice->coreaudio.isSwitchingDevice = MAL_TRUE; + mal_result reinitResult = mal_device_reinit_internal__coreaudio(pDevice, MAL_TRUE); + pDevice->coreaudio.isSwitchingDevice = MAL_FALSE; + + if (reinitResult == MAL_SUCCESS) { + mal_device__post_init_setup(pDevice); + + // Make sure we resume the device if applicable. + if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { + mal_result startResult = pDevice->pContext->onDeviceStart(pDevice); + if (startResult != MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + } + } + } + } + } + + return noErr; +} +#endif + +typedef struct +{ + // Input. + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 sampleRateIn; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesIn; + mal_uint32 bufferSizeInMillisecondsIn; + mal_uint32 periodsIn; + mal_bool32 usingDefaultFormat; + mal_bool32 usingDefaultChannels; + mal_bool32 usingDefaultSampleRate; + mal_bool32 usingDefaultChannelMap; + mal_share_mode shareMode; + + // Output. +#if defined(MAL_APPLE_DESKTOP) + AudioObjectID deviceObjectID; +#endif + AudioComponent component; + AudioUnit audioUnit; + AudioBufferList* pAudioBufferList; // Only used for input devices. + mal_format formatOut; + mal_uint32 channelsOut; + mal_uint32 sampleRateOut; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFramesOut; + mal_uint32 periodsOut; + mal_bool32 exclusiveMode; + char deviceName[256]; +} mal_device_init_internal_data__coreaudio; + +mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ { mal_assert(pContext != NULL); - mal_assert(pConfig != NULL); - mal_assert(pDevice != NULL); mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture); +#if defined(MAL_APPLE_DESKTOP) + pData->deviceObjectID = 0; +#endif + pData->component = NULL; + pData->audioUnit = NULL; + pData->pAudioBufferList = NULL; + mal_result result; #if defined(MAL_APPLE_DESKTOP) @@ -14124,15 +15110,16 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev return result; } - pDevice->coreaudio.deviceObjectID = deviceObjectID; + pData->deviceObjectID = deviceObjectID; #endif // Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. - if (pDevice->periods < 1) { - pDevice->periods = 1; + pData->periodsOut = pData->periodsIn; + if (pData->periodsOut < 1) { + pData->periodsOut = 1; } - if (pDevice->periods > 16) { - pDevice->periods = 16; + if (pData->periodsOut > 16) { + pData->periodsOut = 16; } @@ -14148,14 +15135,14 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev desc.componentFlags = 0; desc.componentFlagsMask = 0; - pDevice->coreaudio.component = ((mal_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pDevice->coreaudio.component == NULL) { + pData->component = ((mal_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (pData->component == NULL) { return MAL_FAILED_TO_INIT_BACKEND; } // Audio unit. - OSStatus status = ((mal_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pDevice->coreaudio.component, (AudioUnit*)&pDevice->coreaudio.audioUnit); + OSStatus status = ((mal_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(pData->component, (AudioUnit*)&pData->audioUnit); if (status != noErr) { return mal_result_from_OSStatus(status); } @@ -14167,25 +15154,25 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev enableIOFlag = 0; } - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); } enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MAL_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MAL_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); } // Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. #if defined(MAL_APPLE_DESKTOP) - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID)); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(result); } #endif @@ -14206,9 +15193,9 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev AudioUnitElement formatElement = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; #if defined(MAL_APPLE_DESKTOP) - result = mal_device_find_best_format__coreaudio(pDevice, &bestFormat); + result = mal_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &bestFormat); if (result != MAL_SUCCESS) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } @@ -14216,28 +15203,28 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev AudioStreamBasicDescription origFormat; UInt32 origFormatSize = sizeof(origFormat); if (deviceType == mal_device_type_playback) { - status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MAL_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); } else { - status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MAL_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MAL_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); } if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } bestFormat.mSampleRate = origFormat.mSampleRate; - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { // We failed to set the format, so fall back to the current format of the audio unit. bestFormat = origFormat; } #else UInt32 propSize = sizeof(bestFormat); - status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); } @@ -14249,76 +15236,54 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; mal_assert(pAudioSession != NULL); - [pAudioSession setPreferredSampleRate:(double)pDevice->sampleRate error:nil]; + [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; bestFormat.mSampleRate = pAudioSession.sampleRate; } - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); } #endif - result = mal_format_from_AudioStreamBasicDescription(&bestFormat, &pDevice->internalFormat); + result = mal_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); if (result != MAL_SUCCESS) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } - if (pDevice->internalFormat == mal_format_unknown) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + if (pData->formatOut == mal_format_unknown) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return MAL_FORMAT_NOT_SUPPORTED; } - pDevice->internalChannels = bestFormat.mChannelsPerFrame; - pDevice->internalSampleRate = bestFormat.mSampleRate; + pData->channelsOut = bestFormat.mChannelsPerFrame; + pData->sampleRateOut = bestFormat.mSampleRate; } // Internal channel map. #if defined(MAL_APPLE_DESKTOP) - result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pDevice->internalChannelMap); + result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut); if (result != MAL_SUCCESS) { return result; } #else // TODO: Figure out how to get the channel map using AVAudioSession. - mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); + mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); #endif // Buffer size. Not allowing this to be configurable on iOS. - mal_uint32 actualBufferSizeInFrames = pDevice->bufferSizeInFrames; - if (actualBufferSizeInFrames < pDevice->periods) { - actualBufferSizeInFrames = pDevice->periods; - } + mal_uint32 actualBufferSizeInFrames = pData->bufferSizeInFramesIn; #if defined(MAL_APPLE_DESKTOP) - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my admittedly limited testing, capture latency seems to be about the same as playback with Core Audio, at least on my MacBook Pro. On other - // backends, however, this is often different. I am therefore leaving the logic below in place just in case I need to do some capture/playback - // specific tweaking. - float fDeviceType; - if (deviceType == mal_device_type_playback) { - fDeviceType = 1.0f; - } else { - fDeviceType = 6.0f; - } - - // Backend tax. Need to fiddle with this. - float fBackend = 1.0f; - - actualBufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fDeviceType*fBackend); - if (actualBufferSizeInFrames < pDevice->periods) { - actualBufferSizeInFrames = pDevice->periods; - } + if (actualBufferSizeInFrames == 0) { + actualBufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut); } - actualBufferSizeInFrames = actualBufferSizeInFrames / pDevice->periods; + actualBufferSizeInFrames = actualBufferSizeInFrames / pData->periodsOut; result = mal_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualBufferSizeInFrames); if (result != MAL_SUCCESS) { return result; @@ -14327,7 +15292,7 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev actualBufferSizeInFrames = 4096; #endif - pDevice->bufferSizeInFrames = actualBufferSizeInFrames * pDevice->periods; + pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut; // During testing I discovered that the buffer size can be too big. You'll get an error like this: // @@ -14339,47 +15304,19 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev /*AudioUnitScope propScope = (deviceType == mal_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; AudioUnitElement propBus = (deviceType == mal_device_type_playback) ? MAL_COREAUDIO_OUTPUT_BUS : MAL_COREAUDIO_INPUT_BUS; - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); }*/ - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames)); if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); } } - - // Callbacks. - AURenderCallbackStruct callbackInfo; - callbackInfo.inputProcRefCon = pDevice; - if (deviceType == mal_device_type_playback) { - callbackInfo.inputProc = mal_on_output__coreaudio; - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MAL_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); - return mal_result_from_OSStatus(status); - } - } else { - callbackInfo.inputProc = mal_on_input__coreaudio; - status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MAL_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); - return mal_result_from_OSStatus(status); - } - } - - // We need to listen for stop events. - status = ((mal_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)((AudioUnit)pDevice->coreaudio.audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice); - if (status != noErr) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); - return mal_result_from_OSStatus(status); - } - - // We need a buffer list if this is an input device. We render into this in the input callback. if (deviceType == mal_device_type_capture) { mal_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; @@ -14388,49 +15325,194 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev if (isInterleaved) { // Interleaved case. This is the simple case because we just have one buffer. allocationSize += sizeof(AudioBuffer) * 1; - allocationSize += actualBufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + allocationSize += actualBufferSizeInFrames * mal_get_bytes_per_frame(pData->formatOut, pData->channelsOut); } else { // Non-interleaved case. This is the more complex case because there's more than one buffer. - allocationSize += sizeof(AudioBuffer) * pDevice->internalChannels; - allocationSize += actualBufferSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat) * pDevice->internalChannels; + allocationSize += sizeof(AudioBuffer) * pData->channelsOut; + allocationSize += actualBufferSizeInFrames * mal_get_bytes_per_sample(pData->formatOut) * pData->channelsOut; } AudioBufferList* pBufferList = (AudioBufferList*)mal_malloc(allocationSize); if (pBufferList == NULL) { - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return MAL_OUT_OF_MEMORY; } if (isInterleaved) { pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = pDevice->internalChannels; - pBufferList->mBuffers[0].mDataByteSize = actualBufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut; + pBufferList->mBuffers[0].mDataByteSize = actualBufferSizeInFrames * mal_get_bytes_per_frame(pData->formatOut, pData->channelsOut); pBufferList->mBuffers[0].mData = (mal_uint8*)pBufferList + sizeof(AudioBufferList); } else { - pBufferList->mNumberBuffers = pDevice->internalChannels; + pBufferList->mNumberBuffers = pData->channelsOut; for (mal_uint32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { pBufferList->mBuffers[iBuffer].mNumberChannels = 1; - pBufferList->mBuffers[iBuffer].mDataByteSize = actualBufferSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat); - pBufferList->mBuffers[iBuffer].mData = (mal_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pDevice->internalChannels)) + (actualBufferSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat) * iBuffer); + pBufferList->mBuffers[iBuffer].mDataByteSize = actualBufferSizeInFrames * mal_get_bytes_per_sample(pData->formatOut); + pBufferList->mBuffers[iBuffer].mData = (mal_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualBufferSizeInFrames * mal_get_bytes_per_sample(pData->formatOut) * iBuffer); } } - pDevice->coreaudio.pAudioBufferList = pBufferList; + pData->pAudioBufferList = pBufferList; } + // Callbacks. + AURenderCallbackStruct callbackInfo; + callbackInfo.inputProcRefCon = pDevice_DoNotReference; + if (deviceType == mal_device_type_playback) { + callbackInfo.inputProc = mal_on_output__coreaudio; + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MAL_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + } else { + callbackInfo.inputProc = mal_on_input__coreaudio; + status = ((mal_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MAL_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + } - // Initialize the audio unit. - status = ((mal_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)((AudioUnit)pDevice->coreaudio.audioUnit); + // We need to listen for stop events. + status = ((mal_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); if (status != noErr) { - mal_free(pDevice->coreaudio.pAudioBufferList); - ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return mal_result_from_OSStatus(status); } + // Initialize the audio unit. + status = ((mal_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); + if (status != noErr) { + mal_free(pData->pAudioBufferList); + pData->pAudioBufferList = NULL; + ((mal_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return mal_result_from_OSStatus(status); + } + + // Grab the name. +#if defined(MAL_APPLE_DESKTOP) + mal_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); +#endif + + return result; +} + +mal_result mal_device_reinit_internal__coreaudio(mal_device* pDevice, mal_bool32 disposePreviousAudioUnit) +{ + mal_device_init_internal_data__coreaudio data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + // We have successfully initialized the new objects. We now need to uninitialize the previous objects and re-set them. + if (disposePreviousAudioUnit) { + pDevice->pContext->onDeviceStop(pDevice); + ((mal_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + } + if (pDevice->coreaudio.pAudioBufferList) { + mal_free(pDevice->coreaudio.pAudioBufferList); + } + +#if defined(MAL_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectID = (mal_uint32)data.deviceObjectID; +#endif + pDevice->coreaudio.component = (mal_ptr)data.component; + pDevice->coreaudio.audioUnit = (mal_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (mal_ptr)data.pAudioBufferList; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = MAL_FALSE; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + + return MAL_SUCCESS; +} + + +mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pConfig; + + mal_assert(pContext != NULL); + mal_assert(pConfig != NULL); + mal_assert(pDevice != NULL); + mal_assert(deviceType == mal_device_type_playback || deviceType == mal_device_type_capture); + + mal_device_init_internal_data__coreaudio data; + data.formatIn = pDevice->format; + data.channelsIn = pDevice->channels; + data.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(data.channelMapIn, pDevice->channelMap, sizeof(pDevice->channelMap)); + data.bufferSizeInFramesIn = pDevice->bufferSizeInFrames; + data.bufferSizeInMillisecondsIn = pDevice->bufferSizeInMilliseconds; + data.periodsIn = pDevice->periods; + data.usingDefaultFormat = pDevice->usingDefaultFormat; + data.usingDefaultChannels = pDevice->usingDefaultChannels; + data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate; + data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; + data.shareMode = pDevice->initConfig.shareMode; + + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + // We have successfully initialized the new objects. We now need to uninitialize the previous objects and re-set them. + pDevice->pContext->onDeviceStop(pDevice); + ((mal_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnit); + if (pDevice->coreaudio.pAudioBufferList) { + mal_free(pDevice->coreaudio.pAudioBufferList); + } + +#if defined(MAL_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectID = (mal_uint32)data.deviceObjectID; +#endif + pDevice->coreaudio.component = (mal_ptr)data.component; + pDevice->coreaudio.audioUnit = (mal_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (mal_ptr)data.pAudioBufferList; + + pDevice->internalFormat = data.formatOut; + pDevice->internalChannels = data.channelsOut; + pDevice->internalSampleRate = data.sampleRateOut; + mal_copy_memory(pDevice->internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->bufferSizeInFrames = data.bufferSizeInFramesOut; + pDevice->periods = data.periodsOut; + pDevice->exclusiveMode = MAL_FALSE; + mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName); + +#if defined(MAL_APPLE_DESKTOP) + // If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly + // switch the device in the background. + AudioObjectPropertyAddress propAddress; + propAddress.mSelector = (deviceType == mal_device_type_playback) ? kAudioHardwarePropertyDefaultOutputDevice : kAudioHardwarePropertyDefaultInputDevice; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = kAudioObjectPropertyElementMaster; + ((mal_AudioObjectAddPropertyListener_proc)pDevice->pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &mal_default_output_device_changed__coreaudio, pDevice); +#endif return MAL_SUCCESS; } + mal_result mal_device__start_backend__coreaudio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -14493,7 +15575,8 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) pContext->coreaudio.AudioObjectGetPropertyData = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); pContext->coreaudio.AudioObjectGetPropertyDataSize = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); pContext->coreaudio.AudioObjectSetPropertyData = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - + pContext->coreaudio.AudioObjectAddPropertyListener = mal_dlsym(pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + // It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still // defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. @@ -14534,6 +15617,7 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) pContext->coreaudio.AudioObjectGetPropertyData = (mal_proc)AudioObjectGetPropertyData; pContext->coreaudio.AudioObjectGetPropertyDataSize = (mal_proc)AudioObjectGetPropertyDataSize; pContext->coreaudio.AudioObjectSetPropertyData = (mal_proc)AudioObjectSetPropertyData; + pContext->coreaudio.AudioObjectAddPropertyListener = (mal_proc)AudioObjectAddPropertyListener; #endif pContext->coreaudio.AudioComponentFindNext = (mal_proc)AudioComponentFindNext; @@ -14565,6 +15649,1489 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) +/////////////////////////////////////////////////////////////////////////////// +// +// sndio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_SNDIO +#include +#include + +// 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 +//#endif +//#if defined(__FreeBSD__) || defined(__DragonFly__) +//#include +//#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 (desiredBufferSizeInFrames == 0) { + desiredBufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, par.rate); + } + + 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); + + // The device is always shared with sndio. + pDevice->exclusiveMode = MAL_FALSE; + + 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 + + + +/////////////////////////////////////////////////////////////////////////////// +// +// audio(4) Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_AUDIO4 +#include +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) + #include + #if defined(OpenBSD) && OpenBSD >= 201709 + #define MAL_AUDIO4_USE_NEW_API + #endif +#endif + +void mal_construct_device_id__audio4(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__audio4(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__audio4(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->audio4, pID1->audio4) == 0; +} + +#if !defined(MAL_AUDIO4_USE_NEW_API) +mal_format mal_format_from_encoding__audio4(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__audio4(struct audio_prinfo* prinfo) +{ + return mal_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); +} +#else +mal_format mal_format_from_swpar__audio4(struct audio_swpar* par) +{ + if (par->bits == 8 && par->bps == 1 && par->sig == 0) { + return mal_format_u8; + } + if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == mal_is_little_endian()) { + return mal_format_s16; + } + if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == mal_is_little_endian()) { + return mal_format_s24; + } + if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == mal_is_little_endian()) { + return mal_format_f32; + } + + // Format not supported. + return mal_format_unknown; +} +#endif + +mal_result mal_context_get_device_info_from_fd__audio4(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); + +#if !defined(MAL_AUDIO4_USE_NEW_API) + // 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__audio4(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; + } +#else + struct audio_swpar fdPar; + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + return MAL_ERROR; + } + + mal_format format = mal_format_from_swpar__audio4(&fdPar); + if (format == mal_format_unknown) { + return MAL_FORMAT_NOT_SUPPORTED; + } + pInfoOut->formats[pInfoOut->formatCount++] = format; + + if (deviceType == mal_device_type_playback) { + pInfoOut->minChannels = fdPar.pchan; + pInfoOut->maxChannels = fdPar.pchan; + } else { + pInfoOut->minChannels = fdPar.rchan; + pInfoOut->maxChannels = fdPar.rchan; + } + + pInfoOut->minSampleRate = fdPar.rate; + pInfoOut->maxSampleRate = fdPar.rate; +#endif + + return MAL_SUCCESS; +} + +mal_result mal_context_enumerate_devices__audio4(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__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (mal_context_get_device_info_from_fd__audio4(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__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (mal_context_get_device_info_from_fd__audio4(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__audio4(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__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); + if (result != MAL_SUCCESS) { + return result; + } + + mal_construct_device_id__audio4(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.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); + } else { + mal_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); + } + + mal_result result = mal_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); + + close(fd); + return result; +} + +void mal_device_uninit__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + close(pDevice->audio4.fd); + mal_free(pDevice->audio4.pIntermediaryBuffer); +} + +mal_result mal_device_init__audio4(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->audio4); + pDevice->audio4.fd = -1; + + // The first thing to do is open the file. + const char* deviceName = "/dev/audio"; + if (pDeviceID != NULL) { + deviceName = pDeviceID->audio4; + } + + pDevice->audio4.fd = open(deviceName, ((deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY) | O_NONBLOCK, 0); + if (pDevice->audio4.fd == -1) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + +#if !defined(MAL_AUDIO4_USE_NEW_API) + 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 audio4 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->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] 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->audio4.fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + if (ioctl(pDevice->audio4.fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_prinfo__audio4(prinfo); + if (pDevice->internalFormat == mal_format_unknown) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] 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->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + + // What mini_al calls a fragment, audio4 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.hiwat = mal_max(pDevice->periods, 5); + fdInfo.lowat = (unsigned int)(fdInfo.hiwat * 0.75); + fdInfo.blocksize = fragmentSizeInBytes / fdInfo.hiwat; + if (ioctl(pDevice->audio4.fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] 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->audio4.fragmentSizeInFrames = fdInfo.blocksize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); +#else + // We need to retrieve the format of the device so we can know the channel count and sample rate. Then we + // can calculate the buffer size. + struct audio_swpar fdPar; + if (ioctl(pDevice->audio4.fd, AUDIO_GETPAR, &fdPar) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MAL_FORMAT_NOT_SUPPORTED); + } + + // Set the initial internal formats so we can do calculations below. + pDevice->internalFormat = mal_format_from_swpar__audio4(&fdPar); + if (deviceType == mal_device_type_playback) { + pDevice->internalChannels = fdPar.pchan; + } else { + pDevice->internalChannels = fdPar.rchan; + } + pDevice->internalSampleRate = fdPar.rate; + + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + + // What mini_al calls a fragment, audio4 calls a block. + mal_uint32 bufferSizeInBytes = pDevice->bufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + if (bufferSizeInBytes < 16) { + bufferSizeInBytes = 16; + } + + fdPar.nblks = pDevice->periods; + fdPar.round = bufferSizeInBytes / fdPar.nblks; + + if (ioctl(pDevice->audio4.fd, AUDIO_SETPAR, &fdPar) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MAL_FORMAT_NOT_SUPPORTED); + } + + if (ioctl(pDevice->audio4.fd, AUDIO_GETPAR, &fdPar) < 0) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_format_from_swpar__audio4(&fdPar); + if (deviceType == mal_device_type_playback) { + pDevice->internalChannels = fdPar.pchan; + } else { + pDevice->internalChannels = fdPar.rchan; + } + pDevice->internalSampleRate = fdPar.rate; + + pDevice->periods = fdPar.nblks; + pDevice->bufferSizeInFrames = (fdPar.nblks * fdPar.round) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + pDevice->audio4.fragmentSizeInFrames = fdPar.round / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); +#endif + + + // For the channel map, I'm not sure how to query the channel map (or if it's even possible). I'm just + // using the channels defined in FreeBSD's sound(4) man page. + mal_get_standard_channel_map(mal_standard_channel_map_sound4, pDevice->internalChannels, pDevice->internalChannelMap); + + + // The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD + // introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as + // I'm aware. +#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 + pDevice->exclusiveMode = MAL_FALSE; +#else + pDevice->exclusiveMode = MAL_TRUE; +#endif + + + // 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->audio4.pIntermediaryBuffer = mal_malloc(pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (pDevice->audio4.pIntermediaryBuffer == NULL) { + close(pDevice->audio4.fd); + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + + + return MAL_SUCCESS; +} + +mal_result mal_device__start_backend__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->audio4.fd == -1) { + return MAL_INVALID_ARGS; + } + + // 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->audio4.fragmentSizeInFrames, pDevice->audio4.pIntermediaryBuffer); + + int bytesWritten = write(pDevice->audio4.fd, pDevice->audio4.pIntermediaryBuffer, pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels)); + if (bytesWritten == -1) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] 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__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->audio4.fd == -1) { + return MAL_INVALID_ARGS; + } + +#if !defined(MAL_AUDIO4_USE_NEW_API) + if (ioctl(pDevice->audio4.fd, AUDIO_FLUSH, 0) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } +#else + if (ioctl(pDevice->audio4.fd, AUDIO_STOP, 0) < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } +#endif + + return MAL_SUCCESS; +} + +mal_result mal_device__break_main_loop__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->audio4.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +mal_result mal_device__wait__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + struct pollfd fds[1]; + fds[0].fd = pDevice->audio4.fd; + fds[0].events = (pDevice->type == mal_device_type_playback) ? (POLLOUT | POLLWRBAND) : (POLLIN | POLLPRI); + int timeout = 2 * 1000; + int ioresult = poll(fds, mal_countof(fds), timeout); + if (ioresult < 0) { + #ifdef MAL_DEBUG_OUTPUT + printf("poll() failed: timeout=%d, ioresult=%d\n", pDevice->bufferSizeInMilliseconds, ioresult); + #endif + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to wait for device.", MAL_ERROR); + } + + // Check for a timeout. This has been annoying in my testing. In my testing, when the device is unplugged it will just + // hang on the next calls to write(), ioctl(), etc. The only way I have figured out how to handle this is to wait for + // a timeout from poll(). In the unplugging case poll() will timeout, however there's no indication that the device is + // unusable - no flags are set, no errors are reported, nothing. To work around this I have decided to outright fail + // in the event of a timeout. + if (ioresult == 0) { + // Check for errors. + if ((fds[0].revents & (POLLERR | POLLNVAL)) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to wait for device.", MAL_NO_DEVICE); + } + if ((fds[0].revents & (POLLHUP)) != 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to wait for device. Disconnected.", MAL_NO_DEVICE); + } + + // A return value of 0 from poll indicates a timeout. + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Timeout while waiting for device.", MAL_TIMEOUT); + } + + mal_assert(ioresult > 0); + return MAL_SUCCESS; +} + +mal_result mal_device__main_loop__audio4(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->audio4.breakFromMainLoop = MAL_FALSE; + while (!pDevice->audio4.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->audio4.fragmentSizeInFrames, pDevice->audio4.pIntermediaryBuffer); + + // Wait for data to become available. + mal_result result = mal_device__wait__audio4(pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + size_t bytesToWrite = pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + while (bytesToWrite > 0) { + ssize_t bytesWritten = write(pDevice->audio4.fd, pDevice->audio4.pIntermediaryBuffer, bytesToWrite); + if (bytesWritten < 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + + if (bytesWritten == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to write any data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + + bytesToWrite -= bytesWritten; + } + } else { + // Capture. + mal_result result = mal_device__wait__audio4(pDevice); + if (result != MAL_SUCCESS) { + return result; + } + + size_t totalBytesRead = 0; + size_t bytesToRead = pDevice->audio4.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + while (bytesToRead > 0) { + ssize_t bytesRead = read(pDevice->audio4.fd, pDevice->audio4.pIntermediaryBuffer, bytesToRead); + if (bytesRead < 0) { + if (errno == EAGAIN) { + break; + } + + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + if (bytesRead == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audio4] Failed to read any data from the device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + bytesToRead -= bytesRead; + totalBytesRead += bytesRead; + } + + mal_uint32 framesRead = (mal_uint32)totalBytesRead / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); + mal_device__send_frames_to_client(pDevice, framesRead, pDevice->audio4.pIntermediaryBuffer); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__audio4(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_audio4); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_init__audio4(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->onUninit = mal_context_uninit__audio4; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__audio4; + pContext->onEnumDevices = mal_context_enumerate_devices__audio4; + pContext->onGetDeviceInfo = mal_context_get_device_info__audio4; + pContext->onDeviceInit = mal_device_init__audio4; + pContext->onDeviceUninit = mal_device_uninit__audio4; + pContext->onDeviceStart = mal_device__start_backend__audio4; + pContext->onDeviceStop = mal_device__stop_backend__audio4; + pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__audio4; + pContext->onDeviceMainLoop = mal_device__main_loop__audio4; + + return MAL_SUCCESS; +} +#endif // audio4 + + /////////////////////////////////////////////////////////////////////////////// // // OSS Backend @@ -14747,10 +17314,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 +17371,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 +17384,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); @@ -14848,22 +17425,8 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con // Try calculating an appropriate default buffer size. - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; - } - - // Backend tax. Need to fiddle with this. - float fBackend = 1.0; - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); } // The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if @@ -14895,9 +17458,11 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con pDevice->periods = (mal_uint32)(ossFragment >> 16); pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->oss.fragmentSizeInFrames * pDevice->periods); - // Set the internal channel map. Not sure if this can be queried. For now just using our default assumptions. - mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); + // Set the internal channel map. Not sure if this can be queried. For now just using the channel layouts defined in FreeBSD's sound(4) man page. + mal_get_standard_channel_map(mal_standard_channel_map_sound4, pDevice->internalChannels, pDevice->internalChannelMap); + // OSS seems to be shared. + pDevice->exclusiveMode = MAL_FALSE; // When not using MMAP mode, we need to use an intermediary buffer for the client <-> device transfer. We do // everything by the size of a fragment. @@ -15489,29 +18054,6 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, mal_assert(pDevice != NULL); mal_zero_object(&pDevice->opensl); - // Try calculating an appropriate default buffer size. - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; - } - - // Backend tax. Need to fiddle with this. - float fBackend = 1.5f; - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); - } - - pDevice->opensl.currentBufferIndex = 0; - pDevice->opensl.periodSizeInFrames = pDevice->bufferSizeInFrames / pConfig->periods; - pDevice->bufferSizeInFrames = pDevice->opensl.periodSizeInFrames * pConfig->periods; - SLDataLocator_AndroidSimpleBufferQueue queue; queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; queue.numBuffers = pConfig->periods; @@ -15538,7 +18080,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 +18133,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; @@ -15734,6 +18277,14 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, pDevice->internalSampleRate = pFormat->samplesPerSec / 1000; mal_channel_mask_to_channel_map__opensl(pFormat->channelMask, pDevice->internalChannels, pDevice->internalChannelMap); + // Try calculating an appropriate default buffer size. + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->internalSampleRate); + } + + pDevice->opensl.currentBufferIndex = 0; + pDevice->opensl.periodSizeInFrames = pDevice->bufferSizeInFrames / pConfig->periods; + pDevice->bufferSizeInFrames = pDevice->opensl.periodSizeInFrames * pConfig->periods; size_t bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat); pDevice->opensl.pBuffer = (mal_uint8*)mal_malloc(bufferSizeInBytes); @@ -16246,22 +18797,12 @@ mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, } // Try calculating an appropriate default buffer size. - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 3; + pDevice->bufferSizeInFrames = mal_scale_buffer_size(pDevice->bufferSizeInFrames, bufferSizeScaleFactor); } - - // Backend tax. Need to fiddle with this. OpenAL has bad latency in my testing :( - float fBackend = 8.0; - - pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); } mal_ALCsizei bufferSizeInSamplesAL = pDevice->bufferSizeInFrames; @@ -17178,32 +19719,13 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con (void)pContext; + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + } + // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need // to explicitly clamp this because it will be easy to overflow. mal_uint32 bufferSize = pConfig->bufferSizeInFrames; - if (pDevice->usingDefaultBufferSize) { - // CPU speed is a factor to consider when determine how large of a buffer we need. - float fCPUSpeed = mal_calculate_cpu_speed_factor(); - - // In my testing, capture seems to have worse latency than playback for some reason. - float fType; - if (type == mal_device_type_playback) { - fType = 1.0f; - } else { - fType = 2.0f; - } - - // Backend tax. Need to fiddle with this. Keep in mind that SDL always rounds the buffer size up to the next - // power of two which should cover the natural API overhead. Special case for Emscripten. - #if defined(__EMSCRIPTEN__) - float fBackend = 1.0f; - #else - float fBackend = 1.0f; - #endif - - bufferSize = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pConfig->sampleRate, fCPUSpeed*fType*fBackend); - } - if (bufferSize > 32768) { bufferSize = 32768; } else { @@ -17265,10 +19787,11 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); pDevice->internalChannels = obtainedSpec.channels; pDevice->internalSampleRate = (mal_uint32)obtainedSpec.freq; + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); pDevice->bufferSizeInFrames = obtainedSpec.samples; pDevice->periods = 1; // SDL doesn't seem to tell us what the period count is. Just set this 1. -#if 0 +#ifdef MAL_DEBUG_OUTPUT printf("=== SDL CONFIG ===\n"); printf("REQUESTED -> RECEIVED\n"); printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat)); @@ -17440,65 +19963,163 @@ mal_bool32 mal__is_channel_map_valid(const mal_channel* channelMap, mal_uint32 c } +void mal_device__post_init_setup(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Make sure the internal channel map was set correctly by the backend. If it's not valid, just fall back to defaults. + if (!mal_channel_map_valid(pDevice->internalChannels, pDevice->internalChannelMap)) { + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); + } + + + // If the format/channels/rate is using defaults we need to set these to be the same as the internal config. + if (pDevice->usingDefaultFormat) { + pDevice->format = pDevice->internalFormat; + } + if (pDevice->usingDefaultChannels) { + pDevice->channels = pDevice->internalChannels; + } + if (pDevice->usingDefaultSampleRate) { + pDevice->sampleRate = pDevice->internalSampleRate; + } + if (pDevice->usingDefaultChannelMap) { + mal_copy_memory(pDevice->channelMap, pDevice->internalChannelMap, sizeof(pDevice->channelMap)); + } + + // Buffer size. The backend will have set bufferSizeInFrames. We need to calculate bufferSizeInMilliseconds here. + pDevice->bufferSizeInMilliseconds = pDevice->bufferSizeInFrames / (pDevice->internalSampleRate/1000); + + + // We need a DSP object which is where samples are moved through in order to convert them to the + // format required by the backend. + mal_dsp_config dspConfig = mal_dsp_config_init_new(); + dspConfig.neverConsumeEndOfInput = MAL_TRUE; + dspConfig.pUserData = pDevice; + if (pDevice->type == mal_device_type_playback) { + dspConfig.formatIn = pDevice->format; + dspConfig.channelsIn = pDevice->channels; + dspConfig.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(dspConfig.channelMapIn, pDevice->channelMap, sizeof(dspConfig.channelMapIn)); + dspConfig.formatOut = pDevice->internalFormat; + dspConfig.channelsOut = pDevice->internalChannels; + dspConfig.sampleRateOut = pDevice->internalSampleRate; + mal_copy_memory(dspConfig.channelMapOut, pDevice->internalChannelMap, sizeof(dspConfig.channelMapOut)); + dspConfig.onRead = mal_device__on_read_from_client; + mal_dsp_init(&dspConfig, &pDevice->dsp); + } else { + dspConfig.formatIn = pDevice->internalFormat; + dspConfig.channelsIn = pDevice->internalChannels; + dspConfig.sampleRateIn = pDevice->internalSampleRate; + mal_copy_memory(dspConfig.channelMapIn, pDevice->internalChannelMap, sizeof(dspConfig.channelMapIn)); + dspConfig.formatOut = pDevice->format; + dspConfig.channelsOut = pDevice->channels; + dspConfig.sampleRateOut = pDevice->sampleRate; + mal_copy_memory(dspConfig.channelMapOut, pDevice->channelMap, sizeof(dspConfig.channelMapOut)); + dspConfig.onRead = mal_device__on_read_from_device; + mal_dsp_init(&dspConfig, &pDevice->dsp); + } +} + + mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) { mal_device* pDevice = (mal_device*)pData; mal_assert(pDevice != NULL); #ifdef MAL_WIN32 - mal_CoInitializeEx(pDevice->pContext, NULL, 0); // 0 = COINIT_MULTITHREADED + mal_CoInitializeEx(pDevice->pContext, NULL, MAL_COINIT_VALUE); #endif - // This is only used to prevent posting onStop() when the device is first initialized. - mal_bool32 skipNextStopEvent = MAL_TRUE; + // 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 (;;) { - // At the start of iteration the device is stopped - we must explicitly mark it as such. - pDevice->pContext->onDeviceStop(pDevice); - - if (!skipNextStopEvent) { - mal_stop_proc onStop = pDevice->onStop; - if (onStop) { - onStop(pDevice); - } - } else { - skipNextStopEvent = MAL_FALSE; - } - - - // Let the other threads know that the device has stopped. - mal_device__set_state(pDevice, MAL_STATE_STOPPED); - mal_event_signal(&pDevice->stopEvent); - - // We use an event to wait for a request to wake up. + // 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; - // Just break if we're terminating. + // 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 here means we just started the device and we need to wait for the device to - // either deliver us data (recording) or request more data (playback). + // 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; } - // The thread that requested the device to start playing is waiting for this thread to start the - // device for real, which is now. + // 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. The main loop can be broken with mal_device__break_main_loop(). - pDevice->pContext->onDeviceMainLoop(pDevice); + + // 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(). + mal_result mainLoopResult = pDevice->pContext->onDeviceMainLoop(pDevice); + if (mainLoopResult != MAL_SUCCESS && pDevice->isDefaultDevice && mal_device__get_state(pDevice) == MAL_STATE_STARTED && !pDevice->exclusiveMode) { + // Something has failed during the main loop. It could be that the device has been lost. If it's the default device, + // we can try switching over to the new default device by uninitializing and reinitializing. + mal_result reinitResult = MAL_ERROR; + if (pDevice->pContext->onDeviceReinit) { + reinitResult = pDevice->pContext->onDeviceReinit(pDevice); + } else { + pDevice->pContext->onDeviceStop(pDevice); + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + + pDevice->pContext->onDeviceUninit(pDevice); + mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED); + + reinitResult = pDevice->pContext->onDeviceInit(pDevice->pContext, pDevice->type, NULL, &pDevice->initConfig, pDevice); + } + + // Perform the post initialization setup just in case the data conversion pipeline needs to be reinitialized. + if (reinitResult == MAL_SUCCESS) { + mal_device__post_init_setup(pDevice); + } + + // If reinitialization was successful, loop back to the start. + if (reinitResult == MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STARTING); // <-- The device is restarting. + mal_event_signal(&pDevice->wakeupEvent); + continue; + } + } + + + // Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this + // may have actually already happened above if the device was lost and mini_al has attempted to re-initialize the device. In this case we + // don't want to be doing this a second time. + if (mal_device__get_state(pDevice) != MAL_STATE_UNINITIALIZED) { + 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. Note that + // it's possible that the device has been uninitialized which means we need to _not_ change the status to stopped. We cannot go from an + // uninitialized state to stopped state. + if (mal_device__get_state(pDevice) != MAL_STATE_UNINITIALIZED) { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->stopEvent); + } } // Make sure we aren't continuously waiting on a stop event. @@ -17569,7 +20190,7 @@ mal_result mal_context_init_backend_apis__win32(mal_context* pContext) pContext->win32.RegQueryValueExA = (mal_proc)mal_dlsym(pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #endif - mal_CoInitializeEx(pContext, NULL, 0); // 0 = COINIT_MULTITHREADED + mal_CoInitializeEx(pContext, NULL, MAL_COINIT_VALUE); return MAL_SUCCESS; } #else @@ -17668,20 +20289,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 +20372,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_AUDIO4 + case mal_backend_audio4: + { + result = mal_context_init__audio4(pContext); + } break; + #endif #ifdef MAL_HAS_OSS case mal_backend_oss: { @@ -18012,6 +20631,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi mal_zero_object(pDevice); pDevice->pContext = pContext; + pDevice->initConfig = config; // Set the user data and log callback ASAP to ensure it is available for the entire initialization process. pDevice->pUserData = pUserData; @@ -18025,6 +20645,10 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi } } + if (pDeviceID == NULL) { + pDevice->isDefaultDevice = MAL_TRUE; + } + // When passing in 0 for the format/channels/rate/chmap it means the device will be using whatever is chosen by the backend. If everything is set // to defaults it means the format conversion pipeline will run on a fast path where data transfer is just passed straight through to the backend. @@ -18045,11 +20669,13 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi } - // Default buffer size and periods. - if (config.bufferSizeInFrames == 0) { - config.bufferSizeInFrames = (config.sampleRate/1000) * MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY; + // Default buffer size. + if (config.bufferSizeInMilliseconds == 0 && config.bufferSizeInFrames == 0) { + config.bufferSizeInMilliseconds = (config.performanceProfile == mal_performance_profile_low_latency) ? MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY : MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE; pDevice->usingDefaultBufferSize = MAL_TRUE; } + + // Default periods. if (config.periods == 0) { config.periods = MAL_DEFAULT_PERIODS; pDevice->usingDefaultPeriods = MAL_TRUE; @@ -18061,6 +20687,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi pDevice->sampleRate = config.sampleRate; mal_copy_memory(pDevice->channelMap, config.channelMap, sizeof(config.channelMap[0]) * config.channels); pDevice->bufferSizeInFrames = config.bufferSizeInFrames; + pDevice->bufferSizeInMilliseconds = config.bufferSizeInMilliseconds; pDevice->periods = config.periods; // The internal format, channel count and sample rate can be modified by the backend. @@ -18100,6 +20727,8 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi return MAL_NO_BACKEND; // The error message will have been posted with mal_post_error() by the source of the error so don't bother calling it here. } + mal_device__post_init_setup(pDevice); + // If the backend did not fill out a name for the device, try a generic method. if (pDevice->name[0] == '\0') { @@ -18121,51 +20750,6 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi } } - // If the format/channels/rate is using defaults we need to set these to be the same as the internal config. - if (pDevice->usingDefaultFormat) { - pDevice->format = pDevice->internalFormat; - } - if (pDevice->usingDefaultChannels) { - pDevice->channels = pDevice->internalChannels; - } - if (pDevice->usingDefaultSampleRate) { - pDevice->sampleRate = pDevice->internalSampleRate; - } - if (pDevice->usingDefaultChannelMap) { - mal_copy_memory(pDevice->channelMap, pDevice->internalChannelMap, sizeof(pDevice->channelMap)); - } - - - // We need a DSP object which is where samples are moved through in order to convert them to the - // format required by the backend. - mal_dsp_config dspConfig = mal_dsp_config_init_new(); - dspConfig.neverConsumeEndOfInput = MAL_TRUE; - dspConfig.pUserData = pDevice; - if (type == mal_device_type_playback) { - dspConfig.formatIn = pDevice->format; - dspConfig.channelsIn = pDevice->channels; - dspConfig.sampleRateIn = pDevice->sampleRate; - mal_copy_memory(dspConfig.channelMapIn, pDevice->channelMap, sizeof(dspConfig.channelMapIn)); - dspConfig.formatOut = pDevice->internalFormat; - dspConfig.channelsOut = pDevice->internalChannels; - dspConfig.sampleRateOut = pDevice->internalSampleRate; - mal_copy_memory(dspConfig.channelMapOut, pDevice->internalChannelMap, sizeof(dspConfig.channelMapOut)); - dspConfig.onRead = mal_device__on_read_from_client; - mal_dsp_init(&dspConfig, &pDevice->dsp); - } else { - dspConfig.formatIn = pDevice->internalFormat; - dspConfig.channelsIn = pDevice->internalChannels; - dspConfig.sampleRateIn = pDevice->internalSampleRate; - mal_copy_memory(dspConfig.channelMapIn, pDevice->internalChannelMap, sizeof(dspConfig.channelMapIn)); - dspConfig.formatOut = pDevice->format; - dspConfig.channelsOut = pDevice->channels; - dspConfig.sampleRateOut = pDevice->sampleRate; - mal_copy_memory(dspConfig.channelMapOut, pDevice->channelMap, sizeof(dspConfig.channelMapOut)); - dspConfig.onRead = mal_device__on_read_from_device; - mal_dsp_init(&dspConfig, &pDevice->dsp); - } - - // Some backends don't require the worker thread. if (!mal_context_is_backend_asynchronous(pContext)) { @@ -18181,6 +20765,15 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi mal_device__set_state(pDevice, MAL_STATE_STOPPED); } + +#ifdef MAL_DEBUG_OUTPUT + printf("[WASAPI] %s (%s)\n", pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture"); + printf(" Format: %s -> %s\n", mal_get_format_name(pDevice->format), mal_get_format_name(pDevice->internalFormat)); + printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels); + printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate); +#endif + + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED); return MAL_SUCCESS; } @@ -18387,19 +20980,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 +21042,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 +21444,147 @@ void mal_get_standard_channel_map_vorbis(mal_uint32 channels, mal_channel channe } } +void mal_get_standard_channel_map_sound4(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_BACK_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: + { + 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; + + case 7: + { + 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_BACK_CENTER; + channelMap[6] = MAL_CHANNEL_LFE; + } break; + + case 8: + 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; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + } + + // Remainder. + if (channels > 8) { + for (mal_uint32 iChannel = 8; iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-8)); + } + } +} + +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) @@ -18887,6 +21609,16 @@ 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_sound4: + { + mal_get_standard_channel_map_sound4(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 +25287,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 +25805,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 +25998,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 +26015,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); - 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; - } - if (framesReadFromClient != 0) { - pSRC->sinc.inputFrameCount += framesReadFromClient; + mal_uint32 framesReadFromClient = 0; + if (framesToReadFromClient > 0) { + 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; + } 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 +26479,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 +26759,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,136 +26781,19 @@ void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint } -typedef struct +mal_uint32 mal_get_bytes_per_sample(mal_format format) { - 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; + mal_uint32 sizes[] = { + 0, // unknown + 1, // u8 + 2, // s16 + 3, // s24 + 4, // s32 + 4, // f32 + }; + return sizes[format]; } -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); -} - - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -24211,6 +26863,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); } @@ -25600,21 +28257,36 @@ mal_result mal_sine_wave_init(double amplitude, double periodsPerSecond, mal_uin } mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* pSamples) +{ + return mal_sine_wave_read_ex(pSineWave, count, 1, mal_stream_layout_interleaved, &pSamples); +} + +mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount, mal_uint32 channels, mal_stream_layout layout, float** ppFrames) { if (pSineWave == NULL) { return 0; } - if (pSamples != NULL) { - for (mal_uint64 i = 0; i < count; i += 1) { - pSamples[i] = (float)(sin(pSineWave->time * pSineWave->periodsPerSecond) * pSineWave->amplitude); + if (ppFrames != NULL) { + for (mal_uint64 iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = (float)(sin(pSineWave->time * pSineWave->periodsPerSecond) * pSineWave->amplitude); pSineWave->time += pSineWave->delta; + + if (layout == mal_stream_layout_interleaved) { + for (mal_uint32 iChannel = 0; iChannel < channels; iChannel += 1) { + ppFrames[0][iFrame*channels + iChannel] = s; + } + } else { + for (mal_uint32 iChannel = 0; iChannel < channels; iChannel += 1) { + ppFrames[iChannel][iFrame] = s; + } + } } } else { - pSineWave->time += pSineWave->delta * count; + pSineWave->time += pSineWave->delta * frameCount; } - return count; + return frameCount; } @@ -25624,6 +28296,50 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* // REVISION HISTORY // ================ // +// v0.8.8 - 2018-09-14 +// - Fix Linux build with the ALSA backend. +// - Minor documentation fix. +// +// v0.8.7 - 2018-09-12 +// - Fix a bug with UWP detection. +// +// v0.8.6 - 2018-08-26 +// - Automatically switch the internal device when the default device is unplugged. Note that this is still in the +// early stages and not all backends handle this the same way. As of this version, this will not detect a default +// device switch when changed from the operating system's audio preferences (unless the backend itself handles +// this automatically). This is not supported in exclusive mode. +// - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the +// user switches the default device via the operating system's audio preferences, mini_al will automatically switch +// the internal device to the new default. This is not supported in exclusive mode. +// - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer. +// - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer. +// - Add support for compiling the UWP build as C. +// - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this +// when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0. +// +// v0.8.5 - 2018-08-12 +// - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in +// frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0 +// then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used. +// - Add support for the audio(4) backend to OpenBSD. +// - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the +// Raspberry Pi experience. +// - Fix a bug where an incorrect number of samples is returned from sinc resampling. +// - Add support for setting the value to be passed to internal calls to CoInitializeEx(). +// - WASAPI and WinMM: Stop the device when it is unplugged. +// +// v0.8.4 - 2018-08-06 +// - Add sndio backend for OpenBSD. +// - Add audio(4) 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 b389333b6..8a498139b 100644 --- a/src/models.c +++ b/src/models.c @@ -5,10 +5,10 @@ * CONFIGURATION: * * #define SUPPORT_FILEFORMAT_OBJ -* Selected desired fileformats to be supported for loading. -* * #define SUPPORT_FILEFORMAT_MTL -* Selected desired fileformats to be supported for loading. +* #define SUPPORT_FILEFORMAT_IQM +* #define SUPPORT_FILEFORMAT_GLTF +* Selected desired fileformats to be supported for model data loading. * * #define SUPPORT_MESH_GENERATION * Support procedural mesh generation functions, uses external par_shapes.h library @@ -48,8 +48,20 @@ #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -#define PAR_SHAPES_IMPLEMENTATION -#include "external/par_shapes.h" // Shapes 3d parametric generation +#if defined(SUPPORT_FILEFORMAT_IQM) + #define RIQM_IMPLEMENTATION + #include "external/riqm.h" // IQM file format loading +#endif + +#if defined(SUPPORT_FILEFORMAT_GLTF) + #define CGLTF_IMPLEMENTATION + #include "external/cgltf.h" // glTF file format loading +#endif + +#if defined(SUPPORT_MESH_GENERATION) + #define PAR_SHAPES_IMPLEMENTATION + #include "external/par_shapes.h" // Shapes 3d parametric generation +#endif //---------------------------------------------------------------------------------- // Defines and Macros @@ -75,6 +87,12 @@ static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data #if defined(SUPPORT_FILEFORMAT_MTL) static Material LoadMTL(const char *fileName); // Load MTL material data #endif +#if defined(SUPPORT_FILEFORMAT_GLTF) +static Mesh LoadIQM(const char *fileName); // Load IQM mesh data +#endif +#if defined(SUPPORT_FILEFORMAT_GLTF) +static Mesh LoadGLTF(const char *fileName); // Load GLTF mesh data +#endif //---------------------------------------------------------------------------------- // Module Functions Definition @@ -646,45 +664,54 @@ void UnloadMesh(Mesh *mesh) rlUnloadMesh(mesh); } -// Export mesh as an OBJ file -void ExportMesh(const char *fileName, Mesh mesh) +// Export mesh data to file +void ExportMesh(Mesh mesh, const char *fileName) { - FILE *objFile = fopen(fileName, "wt"); + bool success = false; - fprintf(objFile, "# raylib Mesh OBJ exporter v1.0\n\n"); - fprintf(objFile, "# Mesh exported as triangle faces and not optimized.\n"); - fprintf(objFile, "# Vertex Count: %i\n", mesh.vertexCount); - fprintf(objFile, "# Triangle Count: %i\n\n", mesh.triangleCount); - fprintf(objFile, "# LICENSE: zlib/libpng\n"); - fprintf(objFile, "# Copyright (c) 2018 Ramon Santamaria (@raysan5)\n\n"); - - fprintf(objFile, "g mesh\n"); - - for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) + if (IsFileExtension(fileName, ".obj")) { - fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); + FILE *objFile = fopen(fileName, "wt"); + + fprintf(objFile, "# raylib Mesh OBJ exporter v1.0\n\n"); + fprintf(objFile, "# Mesh exported as triangle faces and not optimized.\n"); + fprintf(objFile, "# Vertex Count: %i\n", mesh.vertexCount); + fprintf(objFile, "# Triangle Count: %i\n\n", mesh.triangleCount); + fprintf(objFile, "# LICENSE: zlib/libpng\n"); + fprintf(objFile, "# Copyright (c) 2018 Ramon Santamaria (@raysan5)\n\n"); + + fprintf(objFile, "g mesh\n"); + + for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) + { + fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); + } + + for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2) + { + fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); + } + + for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) + { + fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); + } + + for (int i = 0; i < mesh.triangleCount; i += 3) + { + fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2); + } + + fprintf(objFile, "\n"); + + fclose(objFile); + + success = true; } - - for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2) - { - fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); - } - - for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) - { - fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); - } - - for (int i = 0; i < mesh.triangleCount; i += 3) - { - fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2); - } - - fprintf(objFile, "\n"); - - fclose(objFile); + else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export mesh vertex data - TraceLog(LOG_INFO, "Mesh saved: %s", fileName); + if (success) TraceLog(LOG_INFO, "Mesh exported successfully: %s", fileName); + else TraceLog(LOG_WARNING, "Mesh could not be exported."); } #if defined(SUPPORT_MESH_GENERATION) @@ -699,7 +726,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) resZ++; // Vertices definition - int vertexCount = resX*resZ*6; // 6 vertex by quad + int vertexCount = resX*resZ; // vertices get reused for the faces Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); for (int z = 0; z < resZ; z++) @@ -718,7 +745,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up; - // TexCoords definition + // TexCoords definition Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2)); for (int v = 0; v < resZ; v++) { @@ -741,7 +768,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) triangles[t++] = i + 1; triangles[t++] = i; - triangles[t++] = i + resX; + triangles[t++] = i + resX; triangles[t++] = i + resX + 1; triangles[t++] = i + 1; } @@ -1771,7 +1798,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 +1864,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 +2233,8 @@ 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); } } @@ -2222,7 +2248,7 @@ static Mesh LoadOBJ(const char *fileName) { Mesh mesh = { 0 }; - char dataType; + char dataType = 0; char comments[200]; int vertexCount = 0; @@ -2245,7 +2271,7 @@ static Mesh LoadOBJ(const char *fileName) // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) while (!feof(objFile)) { - dataType = '\0'; + dataType = 0; fscanf(objFile, "%c", &dataType); switch (dataType) @@ -2631,3 +2657,59 @@ static Material LoadMTL(const char *fileName) return material; } #endif + +#if defined(SUPPORT_FILEFORMAT_GLTF) +// Load IQM mesh data +static Mesh LoadIQM(const char *fileName) +{ + Mesh mesh = { 0 }; + + // TODO: Load IQM file + + return mesh; +} +#endif + +#if defined(SUPPORT_FILEFORMAT_GLTF) +// Load GLTF mesh data +static Mesh LoadGLTF(const char *fileName) +{ + Mesh mesh = { 0 }; + + // GLTF file loading + FILE *gltfFile = fopen(fileName, "rb"); + + if (gltfFile == NULL) + { + TraceLog(LOG_WARNING, "[%s] GLTF file could not be opened", fileName); + return mesh; + } + + fseek(gltfFile, 0, SEEK_END); + int size = ftell(gltfFile); + fseek(gltfFile, 0, SEEK_SET); + + void *buffer = malloc(size); + fread(buffer, size, 1, gltfFile); + + fclose(gltfFile); + + // GLTF data loading + cgltf_options options = {0}; + cgltf_data data; + cgltf_result result = cgltf_parse(&options, buffer, size, &data); + + if (result == cgltf_result_success) + { + printf("Type: %u\n", data.file_type); + printf("Version: %d\n", data.version); + printf("Meshes: %lu\n", data.meshes_count); + } + else TraceLog(LOG_WARNING, "[%s] GLTF data could not be loaded", fileName); + + free(buffer); + cgltf_free(&data); + + return mesh; +} +#endif diff --git a/src/physac.h b/src/physac.h index 6b78fcc62..038361a45 100644 --- a/src/physac.h +++ b/src/physac.h @@ -96,8 +96,6 @@ #define PHYSAC_MAX_VERTICES 24 #define PHYSAC_CIRCLE_VERTICES 24 -#define PHYSAC_DESIRED_DELTATIME 1.0/60.0 -#define PHYSAC_MAX_TIMESTEP 0.02 #define PHYSAC_COLLISION_ITERATIONS 100 #define PHYSAC_PENETRATION_ALLOWANCE 0.05f #define PHYSAC_PENETRATION_CORRECTION 0.4f @@ -196,6 +194,8 @@ 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 void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default 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 +245,18 @@ PHYSACDEF void ClosePhysics(void); #endif // Time management functionality +#include // 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 #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 // Required for: timespec - #include // Required for: clock_gettime() -#elif defined(__APPLE__) // macOS also defines __MACH__ +#elif defined(__APPLE__) // macOS also defines __MACH__ #include // Required for: mach_absolute_time() #endif @@ -279,16 +278,15 @@ static pthread_t physicsThreadId; // Physics thread id #endif static unsigned int usedMemory = 0; // Total allocated dynamic memory static bool physicsThreadEnabled = false; // Physics thread enabled state - static double baseTime = 0.0; // Offset time for MONOTONIC clock static double startTime = 0.0; // Start time in milliseconds -static double deltaTime = 0.0; // Delta time used for physics steps +static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds static double currentTime = 0.0; // Current time in milliseconds static uint64_t frequency = 0; // Hi-res clock frequency static double accumulator = 0.0; // Physics time step delta time accumulator static unsigned int stepsCount = 0; // Total physics steps processed -static Vector2 gravityForce = { 0.0f, 9.81f/1000 }; // Physics world gravity force +static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array @@ -322,13 +320,12 @@ static bool BiasGreaterThan(float valueA, float valueB); static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer -static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in seconds -static double GetCurrentTime(void); // // Get hi-res MONOTONIC time measure in seconds +static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds +static double GetCurrentTime(void); // Get current time measure in milliseconds static int GetRandomNumber(int min, int max); // Returns a random number between min and max (both included) // Math functions -static void MathClamp(double *value, double min, double max); // Clamp a value in a range static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector @@ -356,10 +353,15 @@ 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 + + accumulator = 0.0; } // Returns true if physics thread is currently enabled @@ -604,7 +606,7 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) { int count = vertexData.vertexCount; Vector2 bodyPos = body->position; - Vector2 vertices[count]; + Vector2 *vertices = (Vector2*)malloc(sizeof(Vector2) * count); Mat2 trans = body->shape.transform; for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i]; @@ -696,6 +698,8 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) // Apply force to new physics body PhysicsAddForce(newBody, forceDirection); } + + free(vertices); } } } @@ -914,6 +918,18 @@ PHYSACDEF void ClosePhysics(void) #if !defined(PHYSAC_NO_THREADS) pthread_join(physicsThreadId, NULL); #endif + + // Unitialize physics manifolds dynamic memory allocations + for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); + + // Unitialize physics bodies dynamic memory allocations + for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); + + #if defined(PHYSAC_DEBUG) + if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); + else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); + else printf("[PHYSAC] physics module closed successfully\n"); + #endif } //---------------------------------------------------------------------------------- @@ -1008,49 +1024,13 @@ static void *PhysicsLoop(void *arg) // Initialize physics loop thread values 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 - for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); - - // Unitialize physics bodies dynamic memory allocations - for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); - - #if defined(PHYSAC_DEBUG) - if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); - else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); - else printf("[PHYSAC] physics module closed successfully\n"); - #endif - return NULL; } @@ -1160,6 +1140,38 @@ 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 + const double delta = currentTime - startTime; + + // Store the time elapsed since the last frame began + accumulator += delta; + + // Fixed time stepping loop + while (accumulator >= deltaTime) + { +#ifdef PHYSAC_DEBUG + //printf("currentTime %f, startTime %f, accumulator-pre %f, accumulator-post %f, delta %f, deltaTime %f\n", + // currentTime, startTime, accumulator, accumulator-deltaTime, delta, deltaTime); +#endif + PhysicsStep(); + accumulator -= deltaTime; + } + + // Record the starting of this frame + startTime = currentTime; +} + +PHYSACDEF void SetPhysicsTimeStep(double delta) +{ + deltaTime = delta; +} + // Finds a valid index for a new manifold initialization static int FindAvailableManifoldIndex() { @@ -1551,8 +1563,8 @@ static void IntegratePhysicsForces(PhysicsBody body) if (body->useGravity) { - body->velocity.x += gravityForce.x*(deltaTime/2.0); - body->velocity.y += gravityForce.y*(deltaTime/2.0); + body->velocity.x += gravityForce.x*(deltaTime/1000/2.0); + body->velocity.y += gravityForce.y*(deltaTime/1000/2.0); } if (!body->freezeOrient) body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2.0); @@ -1586,7 +1598,7 @@ static void InitializePhysicsManifolds(PhysicsManifold manifold) // Determine if we should perform a resting collision or not; // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution - if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime, gravityForce.y*deltaTime }) + PHYSAC_EPSILON)) manifold->restitution = 0; + if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) manifold->restitution = 0; } } @@ -1947,13 +1959,6 @@ static int GetRandomNumber(int min, int max) return (rand()%(abs(max - min) + 1) + min); } -// Clamp a value in a range -static inline void MathClamp(double *value, double min, double max) -{ - if (*value < min) *value = min; - else if (*value > max) *value = max; -} - // Returns the cross product of a vector and a value static inline Vector2 MathCross(float value, Vector2 vector) { @@ -2048,4 +2053,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/raylib.h b/src/raylib.h index 8d1389f73..8256b745a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib - A simple and easy-to-use library to learn videogames programming (www.raylib.com) +* raylib - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -136,14 +136,42 @@ #define KEY_LEFT_SHIFT 340 #define KEY_LEFT_CONTROL 341 #define KEY_LEFT_ALT 342 +#define KEY_LEFT_SUPER 343 #define KEY_RIGHT_SHIFT 344 #define KEY_RIGHT_CONTROL 345 #define KEY_RIGHT_ALT 346 -#define KEY_GRAVE 96 -#define KEY_SLASH 47 +#define KEY_RIGHT_SUPER 347 +#define KEY_KB_MENU 348 +#define KEY_LEFT_BRACKET 91 #define KEY_BACKSLASH 92 +#define KEY_RIGHT_BRACKET 93 +#define KEY_GRAVE 96 + +// Keyboard Number Pad Keys +#define KEY_KP_0 320 +#define KEY_KP_1 321 +#define KEY_KP_2 322 +#define KEY_KP_3 323 +#define KEY_KP_4 324 +#define KEY_KP_5 325 +#define KEY_KP_6 326 +#define KEY_KP_7 327 +#define KEY_KP_8 328 +#define KEY_KP_9 329 +#define KEY_KP_DECIMAL 330 +#define KEY_KP_DIVIDE 331 +#define KEY_KP_MULTIPLY 332 +#define KEY_KP_SUBTRACT 333 +#define KEY_KP_ADD 334 +#define KEY_KP_ENTER 335 +#define KEY_KP_EQUAL 336 // Keyboard Alpha Numeric Keys +#define KEY_APOSTROPHE 39 +#define KEY_COMMA 44 +#define KEY_MINUS 45 +#define KEY_PERIOD 46 +#define KEY_SLASH 47 #define KEY_ZERO 48 #define KEY_ONE 49 #define KEY_TWO 50 @@ -154,6 +182,8 @@ #define KEY_SEVEN 55 #define KEY_EIGHT 56 #define KEY_NINE 57 +#define KEY_SEMICOLON 59 +#define KEY_EQUAL 61 #define KEY_A 65 #define KEY_B 66 #define KEY_C 67 @@ -193,7 +223,7 @@ #define MOUSE_MIDDLE_BUTTON 2 // Touch points registered -#define MAX_TOUCH_POINTS 2 +#define MAX_TOUCH_POINTS 10 // Gamepad Number #define GAMEPAD_PLAYER1 0 @@ -281,7 +311,7 @@ // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. -#ifdef __cplusplus +#if defined(__cplusplus) #define CLITERAL #else #define CLITERAL (Color) @@ -324,11 +354,11 @@ //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- -#ifndef __cplusplus // Boolean type - #ifndef bool - typedef enum { false, true } bool; - #endif +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; #endif // Vector2 type @@ -412,6 +442,16 @@ typedef struct RenderTexture2D { // RenderTexture type, same as RenderTexture2D typedef RenderTexture2D RenderTexture; +// N-Patch layout info +typedef struct NPatchInfo { + Rectangle sourceRec; // Region in the texture + int left; // left border offset + int top; // top border offset + int right; // right border offset + int bottom; // bottom border offset + int type; // layout of the n-patch: 3x3, 1x3 or 3x1 +} NPatchInfo; + // Font character info typedef struct CharInfo { int value; // Character value (Unicode) @@ -681,6 +721,13 @@ typedef enum { WRAP_MIRROR } TextureWrapMode; +// Font type, defines generation method +typedef enum { + FONT_DEFAULT = 0, // Default font generation, anti-aliased + FONT_BITMAP, // Bitmap font generation, no anti-aliasing + FONT_SDF // SDF font generation, requires external shader +} FontType; + // Color blending modes (pre-defined) typedef enum { BLEND_ALPHA = 0, @@ -729,10 +776,17 @@ typedef enum { HMD_SONY_PSVR } VrDeviceType; +// Type of n-patch +typedef enum { + NPT_9PATCH = 0, // 3x3 + NPT_3PATCH_VERTICAL, // 1x3 + NPT_3PATCH_HORIZONTAL // 3x1 +} NPatchType; + // Callbacks to be implemented by users typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args); -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { // Prevents name mangling of functions #endif @@ -760,6 +814,13 @@ RLAPI void SetWindowMinSize(int width, int height); // Set window RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height +RLAPI void *GetWindowHandle(void); // Get native window handle +RLAPI int GetMonitorCount(void); // Get number of connected monitors +RLAPI int GetMonitorWidth(int monitor); // Get primary monitor width +RLAPI int GetMonitorHeight(int monitor); // Get primary monitor height +RLAPI int GetMonitorPhysicalWidth(int monitor); // Get primary monitor physical width in millimetres +RLAPI int GetMonitorPhysicalHeight(int monitor); // Get primary monitor physical height in millimetres +RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor // Cursor-related functions RLAPI void ShowCursor(void); // Shows cursor @@ -807,15 +868,20 @@ RLAPI void TakeScreenshot(const char *fileName); // Takes a scr RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) // Files management functions +RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string +RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (memory should be freed) RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) +RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed) +RLAPI void ClearDirectoryFiles(void); // Clear directory files paths buffers (free memory) RLAPI bool ChangeDirectory(const char *dir); // Change working directory, returns true if success RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI char **GetDroppedFiles(int *count); // Get dropped files names -RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer +RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed) +RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) +RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) // Persistent storage management RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) @@ -934,7 +1000,7 @@ RLAPI Image LoadImage(const char *fileName); RLAPI Image LoadImageEx(Color *pixels, int width, int height); // Load image from Color array data (RGBA - 32bit) RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI void ExportImage(const char *fileName, Image image); // Export image as a PNG file +RLAPI void ExportImage(Image image, const char *fileName); // Export image data to file RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) @@ -956,11 +1022,12 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle -RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering) +RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) +RLAPI Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount); // Extract color palette from image to maximum size (memory should be freed) RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image @@ -999,6 +1066,7 @@ RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters +RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely. //------------------------------------------------------------------------------------ // Font Loading and Text Drawing Functions (Module: text) @@ -1008,14 +1076,14 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters -RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, bool sdf); // Load font data for further use -RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info +RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use +RLAPI Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) // Text drawing functions RLAPI void DrawFPS(int posX, int posY); // Shows current FPS RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters +RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters // Text misc. functions RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font @@ -1058,7 +1126,7 @@ RLAPI void UnloadModel(Model model); // Mesh loading/unloading functions RLAPI Mesh LoadMesh(const char *fileName); // Load mesh from file RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) -RLAPI void ExportMesh(const char *fileName, Mesh mesh); // Export mesh as an OBJ file +RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file // Mesh manipulation functions RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits @@ -1166,6 +1234,7 @@ RLAPI Sound LoadSoundFromWave(Wave wave); // Load so RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound +RLAPI void ExportWave(Wave wave, const char *fileName); // Export wave data to file // Wave/Sound management functions RLAPI void PlaySound(Sound sound); // Play a sound @@ -1208,7 +1277,7 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) -#ifdef __cplusplus +#if defined(__cplusplus) } #endif diff --git a/src/raylib.ico b/src/raylib.ico index afeb12b91..0cedcc55c 100644 Binary files a/src/raylib.ico and b/src/raylib.ico differ diff --git a/src/raylib.rc b/src/raylib.rc index ee2a5fabf..2aaa5e26e 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -9,7 +9,7 @@ BEGIN //BLOCK "080904E4" // English UK BLOCK "040904E4" // English US BEGIN - VALUE "CompanyName", "raylib technologies" + //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "Created using raylib (www.raylib.com)" VALUE "FileVersion", "2.0.0" VALUE "InternalName", "raylib app" diff --git a/src/raymath.h b/src/raymath.h index 4e100bc38..1d5e26f9a 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -60,7 +60,13 @@ #endif #ifdef RAYMATH_IMPLEMENTATION - #define RMDEF extern inline // Provide external definition + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RMDEF __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) + #else + #define RMDEF extern inline // Provide external definition + #endif #elif defined RAYMATH_HEADER_ONLY #define RMDEF static inline // Functions may be inlined, no external out-of-line definition #else @@ -149,6 +155,12 @@ RMDEF float Clamp(float value, float min, float max) return res > max ? max : res; } +// Calculate linear interpolation between two vectors +RMDEF float Lerp(float start, float end, float amount) +{ + return start + amount*(end - start); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Vector2 math //---------------------------------------------------------------------------------- @@ -217,6 +229,13 @@ RMDEF Vector2 Vector2Scale(Vector2 v, float scale) return result; } +// Multiply vector by vector +RMDEF Vector2 Vector2MultiplyV(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x*v2.x, v1.y*v2.y }; + return result; +} + // Negate vector RMDEF Vector2 Vector2Negate(Vector2 v) { @@ -231,6 +250,13 @@ RMDEF Vector2 Vector2Divide(Vector2 v, float div) return result; } +// Divide vector by vector +RMDEF Vector2 Vector2DivideV(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x/v2.x, v1.y/v2.y }; + return result; +} + // Normalize provided vector RMDEF Vector2 Vector2Normalize(Vector2 v) { @@ -238,6 +264,17 @@ RMDEF Vector2 Vector2Normalize(Vector2 v) return result; } +// Calculate linear interpolation between two vectors +RMDEF Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) +{ + Vector2 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + + return result; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Vector3 math //---------------------------------------------------------------------------------- @@ -263,7 +300,7 @@ RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) return result; } -// Substract two vectors +// Subtract two vectors RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) { Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; @@ -296,12 +333,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; } @@ -355,6 +392,20 @@ RMDEF Vector3 Vector3Negate(Vector3 v) return result; } +// Divide vector by a float value +RMDEF Vector3 Vector3Divide(Vector3 v, float div) +{ + Vector3 result = { v.x / div, v.y / div, v.z / div }; + return result; +} + +// Divide vector by vector +RMDEF Vector3 Vector3DivideV(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; + return result; +} + // Normalize provided vector RMDEF Vector3 Vector3Normalize(Vector3 v) { @@ -669,8 +720,8 @@ RMDEF Matrix MatrixAdd(Matrix left, Matrix right) return result; } -// Substract two matrices (left - right) -RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) +// Subtract two matrices (left - right) +RMDEF Matrix MatrixSubtract(Matrix left, Matrix right) { Matrix result = MatrixIdentity(); @@ -840,28 +891,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 +934,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 +950,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 +1031,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 +1122,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 d3b916eaf..d97d888e1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -378,7 +378,7 @@ typedef unsigned char byte; } VrDevice; #endif -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { // Prevents name mangling of functions #endif @@ -446,9 +446,11 @@ void rlLoadExtensions(void *loader); // Load OpenGL extensions Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates // Textures data management -unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU -void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data -void rlUnloadTexture(unsigned int id); +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats +void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory + void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) @@ -514,7 +516,7 @@ void TraceLog(int msgType, const char *text, ...); // Show trace log messag int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) #endif -#ifdef __cplusplus +#if defined(__cplusplus) } #endif @@ -551,7 +553,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i #else // APIENTRY for OpenGL function pointer declarations is required #ifndef APIENTRY - #ifdef _WIN32 + #if defined(_WIN32) #define APIENTRY __stdcall #else #define APIENTRY @@ -562,7 +564,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i #define WINGDIAPI __declspec(dllimport) #endif - #include // OpenGL 1.1 library + #include // OpenGL 1.1 library #endif #endif @@ -808,25 +810,25 @@ typedef struct VrStereoConfig { static Matrix stack[MATRIX_STACK_SIZE]; static int stackCounter = 0; -static Matrix modelview; -static Matrix projection; -static Matrix *currentMatrix; -static int currentMatrixMode; +static Matrix modelview = { 0 }; +static Matrix projection = { 0 }; +static Matrix *currentMatrix = NULL; +static int currentMatrixMode = -1; -static int currentDrawMode; +static int currentDrawMode = -1; static float currentDepth = -1.0f; -static DynamicBuffer lines; // Default dynamic buffer for lines data -static DynamicBuffer triangles; // Default dynamic buffer for triangles data -static DynamicBuffer quads; // Default dynamic buffer for quads data (used to draw textures) +static DynamicBuffer lines = { 0 }; // Default dynamic buffer for lines data +static DynamicBuffer triangles = { 0 }; // Default dynamic buffer for triangles data +static DynamicBuffer quads = { 0 }; // Default dynamic buffer for quads data (used to draw textures) // Default buffers draw calls -static DrawCall *draws; -static int drawsCounter; +static DrawCall *draws = NULL; +static int drawsCounter = 0; // Temp vertex buffer to be used with rlTranslate, rlRotate, rlScale -static Vector3 *tempBuffer; +static Vector3 *tempBuffer = NULL; static int tempBufferCount = 0; static bool useTempBuffer = false; @@ -913,9 +915,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); / #endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Get OpenGL internal formats and data type from raylib PixelFormat -static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType); - #if defined(GRAPHICS_API_OPENGL_11) static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight); @@ -1211,15 +1210,27 @@ void rlEnd(void) // Verify internal buffers limits // NOTE: This check is combined with usage of rlCheckBufferLimit() - if ((lines.vCounter/2 >= MAX_LINES_BATCH - 2) || - (triangles.vCounter/3 >= MAX_TRIANGLES_BATCH - 3) || - (quads.vCounter/4 >= MAX_QUADS_BATCH - 4)) rlglDraw(); + if ((lines.vCounter/2 >= (MAX_LINES_BATCH - 2)) || + (triangles.vCounter/3 >= (MAX_TRIANGLES_BATCH - 3)) || + (quads.vCounter/4 >= (MAX_QUADS_BATCH - 4))) + { + // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), + // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! + // Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw + + // TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly + + rlPopMatrix(); + rlglDraw(); + } } // Define one vertex (position) void rlVertex3f(float x, float y, float z) { - if (useTempBuffer) + // NOTE: Temp buffer is processed and resetted at rlEnd() + // Between rlBegin() and rlEnd() can not be more than TEMP_VERTEX_BUFFER_SIZE rlVertex3f() calls + if (useTempBuffer && (tempBufferCount < TEMP_VERTEX_BUFFER_SIZE)) { tempBuffer[tempBufferCount].x = x; tempBuffer[tempBufferCount].y = y; @@ -1381,11 +1392,7 @@ void rlEnableTexture(unsigned int id) { if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++; - if (drawsCounter >= MAX_DRAWS_BY_TEXTURE) - { - rlglDraw(); - drawsCounter = 1; - } + if (drawsCounter >= MAX_DRAWS_BY_TEXTURE) rlglDraw(); draws[drawsCounter - 1].textureId = id; draws[drawsCounter - 1].vertexCount = 0; @@ -1499,9 +1506,17 @@ void rlDeleteTextures(unsigned int id) void rlDeleteRenderTextures(RenderTexture2D target) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (target.id > 0) glDeleteFramebuffers(1, &target.id); if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); - if (target.depth.id > 0) glDeleteTextures(1, &target.depth.id); + if (target.depth.id > 0) + { +#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteRenderbuffers(1, &target.depth.id); +#elif defined(GRAPHICS_API_OPENGL_33) + glDeleteTextures(1, &target.depth.id); +#endif + } + + if (target.id > 0) glDeleteFramebuffers(1, &target.id); TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); #endif @@ -1613,7 +1628,7 @@ void rlglInit(int width, int height) // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); -#ifdef _MSC_VER +#if defined(_MSC_VER) const char **extList = malloc(sizeof(const char *)*numExt); #else const char *extList[numExt]; @@ -1624,17 +1639,13 @@ void rlglInit(int width, int height) #elif defined(GRAPHICS_API_OPENGL_ES2) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string - // NOTE: We have to duplicate string because glGetString() returns a const value - // If not duplicated, it fails in some systems (Raspberry Pi) - // Equivalent to function: char *strdup(const char *str) - char *extensionsDup; - size_t len = strlen(extensions) + 1; - void *newstr = malloc(len); - if (newstr == NULL) extensionsDup = NULL; - extensionsDup = (char *)memcpy(newstr, extensions, len); + // NOTE: We have to duplicate string because glGetString() returns a const string + int len = strlen(extensions) + 1; + char *extensionsDup = (char *)malloc(len); + strcpy(extensionsDup, extensions); // NOTE: String could be splitted using strtok() function (string.h) - // NOTE: strtok() modifies the received string, it can not be const + // NOTE: strtok() modifies the passed string, it can not be const char *extList[512]; // Allocate 512 strings pointers (2 KB) @@ -1765,13 +1776,18 @@ void rlglInit(int width, int height) for (int i = 0; i < MAX_DRAWS_BY_TEXTURE; i++) { - draws[i].textureId = 0; draws[i].vertexCount = 0; + draws[i].vaoId = 0; + draws[i].shaderId = 0; + draws[i].textureId = 0; + + draws[i].projection = MatrixIdentity(); + draws[i].modelview = MatrixIdentity(); } drawsCounter = 1; - draws[drawsCounter - 1].textureId = whiteTexture; - currentDrawMode = RL_TRIANGLES; // Set default draw mode + draws[0].textureId = whiteTexture; // Set default draw texture id + currentDrawMode = RL_TRIANGLES; // Set default draw mode // Init internal matrix stack (emulating OpenGL 1.1) for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); @@ -1828,6 +1844,7 @@ void rlglClose(void) TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); free(draws); + free(tempBuffer); #endif } @@ -2001,8 +2018,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi { unsigned int mipSize = GetPixelDataSize(mipWidth, mipHeight, format); - int glInternalFormat, glFormat, glType; - GetGlFormats(format, &glInternalFormat, &glFormat, &glType); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); TraceLog(LOG_DEBUG, "Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); @@ -2091,8 +2108,8 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v { glBindTexture(GL_TEXTURE_2D, id); - int glInternalFormat, glFormat, glType; - GetGlFormats(format, &glInternalFormat, &glFormat, &glType); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); if ((glInternalFormat != -1) && (format < COMPRESSED_DXT1_RGB)) { @@ -2101,17 +2118,68 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v else TraceLog(LOG_WARNING, "Texture format updating not supported"); } +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) +{ + *glInternalFormat = -1; + *glFormat = -1; + *glType = -1; + + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TraceLog(LOG_WARNING, "Texture format not supported"); break; + } +} + // Unload texture from GPU memory void rlUnloadTexture(unsigned int id) { if (id > 0) glDeleteTextures(1, &id); } - // Load a texture to be used for rendering (fbo with color and depth attachments) RenderTexture2D rlLoadRenderTexture(int width, int height) { - RenderTexture2D target; + RenderTexture2D target = { 0 }; target.id = 0; @@ -2191,8 +2259,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height) default: break; } - glDeleteTextures(1, &target.texture.id); - glDeleteTextures(1, &target.depth.id); + if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); + if (target.depth.id > 0) + { +#if defined(USE_DEPTH_RENDERBUFFER) + glDeleteRenderbuffers(1, &target.depth.id); +#elif defined(USE_DEPTH_TEXTURE) + glDeleteTextures(1, &target.depth.id); +#endif + } + glDeleteFramebuffers(1, &target.id); } else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); @@ -2731,8 +2807,8 @@ void *rlReadTexturePixels(Texture2D texture) // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) glPixelStorei(GL_PACK_ALIGNMENT, 1); - int glInternalFormat, glFormat, glType; - GetGlFormats(texture.format, &glInternalFormat, &glFormat, &glType); + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(texture.format, &glInternalFormat, &glFormat, &glType); unsigned int size = GetPixelDataSize(texture.width, texture.height, texture.format); if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB)) @@ -2818,12 +2894,12 @@ void rlRecordDraw(void) { // TODO: Before adding a new draw, check if anything changed from last stored draw #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + draws[drawsCounter].vertexCount = currentState.vertexCount; draws[drawsCounter].vaoId = currentState.vaoId; // lines.id, trangles.id, quads.id? draws[drawsCounter].textureId = currentState.textureId; // whiteTexture? draws[drawsCounter].shaderId = currentState.shaderId; // defaultShader.id draws[drawsCounter].projection = projection; draws[drawsCounter].modelview = modelview; - draws[drawsCounter].vertexCount = currentState.vertexCount; drawsCounter++; #endif @@ -3306,8 +3382,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 +3709,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(); @@ -3743,7 +3819,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); -#ifdef _MSC_VER +#if defined(_MSC_VER) char *log = malloc(maxLength); #else char log[maxLength]; @@ -3752,7 +3828,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad TraceLog(LOG_INFO, "%s", log); -#ifdef _MSC_VER +#if defined(_MSC_VER) free(log); #endif glDeleteProgram(program); @@ -4271,8 +4347,6 @@ static void DrawBuffersDefault(void) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); } - //TraceLog(LOG_DEBUG, "Draws required per frame: %i", drawsCounter); - for (int i = 0; i < drawsCounter; i++) { quadsCount = draws[i].vertexCount/4; @@ -4309,11 +4383,6 @@ static void DrawBuffersDefault(void) glUseProgram(0); // Unbind shader program } - // Reset draws counter - drawsCounter = 1; - draws[0].textureId = whiteTexture; - draws[0].vertexCount = 0; - // Reset vertex counters for next frame lines.vCounter = 0; lines.cCounter = 0; @@ -4329,6 +4398,11 @@ static void DrawBuffersDefault(void) // Restore projection/modelview matrices projection = matProjection; modelview = matModelView; + + // Reset draws counter + drawsCounter = 1; + draws[0].textureId = whiteTexture; + draws[0].vertexCount = 0; } // Unload default internal buffers vertex data from CPU and GPU @@ -4502,7 +4576,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 +4627,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 @@ -4579,58 +4653,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) #endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Get OpenGL internal formats and data type from raylib PixelFormat -static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType) -{ - *glInternalFormat = -1; - *glFormat = -1; - *glType = -1; - - switch (format) - { - #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA - case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - #if !defined(GRAPHICS_API_OPENGL_11) - case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - #endif - #elif defined(GRAPHICS_API_OPENGL_33) - case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; - case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; - case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; - #endif - #if !defined(GRAPHICS_API_OPENGL_11) - case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; - case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; - case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; - case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; - case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - #endif - default: TraceLog(LOG_WARNING, "Texture format not supported"); break; - } -} - #if defined(GRAPHICS_API_OPENGL_11) // Mipmaps data is generated after image data // NOTE: Only works with RGBA (4 bytes) data! diff --git a/src/shapes.c b/src/shapes.c index 424fb0ba5..90f95826a 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 @@ -647,11 +647,9 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 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)); - - if ((dx <= (rec1.width/2 + rec2.width/2)) && ((dy <= (rec1.height/2 + rec2.height/2)))) collision = true; + + if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) && + (rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true; return collision; } @@ -675,11 +673,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 +698,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 +766,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 e871a6aa7..51e93b720 100644 --- a/src/text.c +++ b/src/text.c @@ -86,9 +86,6 @@ static Font LoadImageFont(Image image, Color key, int firstChar); // Load a Imag #if defined(SUPPORT_FILEFORMAT_FNT) static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) #endif -#if defined(SUPPORT_FILEFORMAT_TTF) -//static Font LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load spritefont from TTF data -#endif #if defined(SUPPORT_DEFAULT_FONT) extern void LoadDefaultFont(void); @@ -220,12 +217,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 +230,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 +241,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); } @@ -279,15 +276,7 @@ Font LoadFont(const char *fileName) Font font = { 0 }; #if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf")) - { - font.baseSize = DEFAULT_TTF_FONTSIZE; - font.charsCount = DEFAULT_TTF_NUMCHARS; - font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, false); - Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0); - font.texture = LoadTextureFromImage(atlas); - UnloadImage(atlas); - } + if (IsFileExtension(fileName, ".ttf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, DEFAULT_TTF_NUMCHARS, NULL); else #endif #if defined(SUPPORT_FILEFORMAT_FNT) @@ -319,17 +308,21 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha font.baseSize = fontSize; font.charsCount = (charsCount > 0) ? charsCount : 95; - font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, false); - Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 0, 0); - font.texture = LoadTextureFromImage(atlas); - UnloadImage(atlas); + font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT); + + if (font.chars != NULL) + { + Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0); + font.texture = LoadTextureFromImage(atlas); + UnloadImage(atlas); + } return font; } // Load font data for further use // NOTE: Requires TTF font and can generate SDF data -CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, bool sdf) +CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type) { // NOTE: Using some SDF generation default values, // trades off precision with ability to handle *smaller* sizes @@ -337,81 +330,98 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c #define SDF_ON_EDGE_VALUE 128 #define SDF_PIXEL_DIST_SCALE 64.0f - // In case no chars count provided, default to 95 - charsCount = (charsCount > 0) ? charsCount : 95; - - CharInfo *chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); + #define BITMAP_ALPHA_THRESHOLD 80 + + CharInfo *chars = NULL; // Load font data (including pixel data) from TTF file // NOTE: Loaded information should be enough to generate font image atlas, // using any packaging method FILE *fontFile = fopen(fileName, "rb"); // Load font file - fseek(fontFile, 0, SEEK_END); - long size = ftell(fontFile); // Get file size - fseek(fontFile, 0, SEEK_SET); // Reset file pointer - - unsigned char *fontBuffer = (unsigned char *)malloc(size); - - fread(fontBuffer, size, 1, fontFile); - fclose(fontFile); - - // Init font for data reading - stbtt_fontinfo fontInfo; - if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!"); - - // Calculate font scale factor - float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, 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; - - // Fill fontChars in case not provided externally - // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space) - int genFontChars = false; - if (fontChars == NULL) genFontChars = true; - if (genFontChars) + if (fontFile != NULL) { - fontChars = (int *)malloc(charsCount*sizeof(int)); - for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; - } - - // NOTE: Using simple packaging, one char after another - for (int i = 0; i < charsCount; i++) - { - int chw = 0, chh = 0; // Character width and height (on generation) - int ch = fontChars[i]; // Character value to get info for - chars[i].value = ch; + fseek(fontFile, 0, SEEK_END); + long size = ftell(fontFile); // Get file size + fseek(fontFile, 0, SEEK_SET); // Reset file pointer - // Render a unicode codepoint to a bitmap - // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap - // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be - // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide + unsigned char *fontBuffer = (unsigned char *)malloc(size); - if (!sdf) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); - else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + fread(fontBuffer, size, 1, fontFile); + fclose(fontFile); - chars[i].rec.width = (float)chw; - chars[i].rec.height = (float)chh; - chars[i].offsetY += ascent; - - // Get bounding box for character (may be offset to account for chars that dip above or below the line) - int chX1, chY1, chX2, chY2; - stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); - - TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1); - TraceLog(LOG_DEBUG, "Character offsetY: %i", ascent + chY1); + // Init font for data reading + stbtt_fontinfo fontInfo; + if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!"); - stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); - chars[i].advanceX *= scaleFactor; + // Calculate font scale factor + 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); + + // In case no chars count provided, default to 95 + charsCount = (charsCount > 0) ? charsCount : 95; + + // Fill fontChars in case not provided externally + // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space) + int genFontChars = false; + if (fontChars == NULL) genFontChars = true; + if (genFontChars) + { + fontChars = (int *)malloc(charsCount*sizeof(int)); + for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; + } + + chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); + + // NOTE: Using simple packaging, one char after another + for (int i = 0; i < charsCount; i++) + { + int chw = 0, chh = 0; // Character width and height (on generation) + int ch = fontChars[i]; // Character value to get info for + chars[i].value = ch; + + // Render a unicode codepoint to a bitmap + // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap + // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be + // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide + + if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); + + if (type == FONT_BITMAP) + { + // Aliased bitmap (black & white) font generation, avoiding anti-aliasing + // NOTE: For optimum results, bitmap font should be generated at base pixel size + for (int p = 0; p < chw*chh; p++) + { + if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0; + else chars[i].data[p] = 255; + } + } + + chars[i].rec.width = (float)chw; + chars[i].rec.height = (float)chh; + chars[i].offsetY += (int)((float)ascent*scaleFactor); + + // Get bounding box for character (may be offset to account for chars that dip above or below the line) + int chX1, chY1, chX2, chY2; + stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); + + TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1); + TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1); + + stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); + chars[i].advanceX *= scaleFactor; + } + + free(fontBuffer); + if (genFontChars) free(fontChars); } - - free(fontBuffer); - if (genFontChars) free(fontChars); + else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName); return chars; } @@ -460,8 +470,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 +512,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) { @@ -519,6 +529,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi else TraceLog(LOG_WARNING, "Character could not be packed: %i", i); } + free(rects); free(nodes); free(context); } @@ -580,10 +591,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int length = strlen(text); int textOffsetX = 0; // Offset between characters int textOffsetY = 0; // Required for line break! - float scaleFactor; + float scaleFactor = 0.0f; - unsigned char letter; // Current character - int index; // Index position in sprite font + unsigned char letter = 0; // Current character + int index = 0; // Index position in sprite font scaleFactor = fontSize/font.baseSize; @@ -647,7 +658,7 @@ const char *FormatText(const char *text, ...) // Get a piece of a text string const char *SubText(const char *text, int position, int length) { - static char buffer[MAX_SUBTEXT_LENGTH]; + static char buffer[MAX_SUBTEXT_LENGTH] = { 0 }; int textLength = strlen(text); if (position >= textLength) @@ -660,11 +671,11 @@ const char *SubText(const char *text, int position, int length) for (int c = 0 ; c < length ; c++) { - *(buffer+c) = *(text+position); + *(buffer + c) = *(text + position); text++; } - *(buffer+length) = '\0'; + *(buffer + length) = '\0'; return buffer; } @@ -694,8 +705,8 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int tempLen = 0; // Used to count longer text line num chars int lenCounter = 0; - float textWidth = 0; - float tempTextWidth = 0; // Used to count longer text line width + float textWidth = 0.0f; + float tempTextWidth = 0.0f; // Used to count longer text line width float textHeight = (float)font.baseSize; float scaleFactor = fontSize/(float)font.baseSize; @@ -834,15 +845,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 +898,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"); @@ -903,17 +914,18 @@ static Font LoadBMFont(const char *fileName) Font font = { 0 }; font.texture.id = 0; - char buffer[MAX_BUFFER_SIZE]; + char buffer[MAX_BUFFER_SIZE] = { 0 }; char *searchPoint = NULL; int fontSize = 0; - int texWidth, texHeight; + int texWidth = 0; + int texHeight = 0; char texFileName[129]; int charsCount = 0; - int base; // Useless data + int base = 0; // Useless data - FILE *fntFile; + FILE *fntFile = NULL; fntFile = fopen(fileName, "rt"); @@ -996,7 +1008,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 3530e115e..a0e74637b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -19,6 +19,9 @@ * Selecte desired fileformats to be supported for image data loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * +* #define SUPPORT_IMAGE_EXPORT +* Support image export in multiple file formats +* * #define SUPPORT_IMAGE_MANIPULATION * Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... * If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() @@ -103,6 +106,11 @@ // NOTE: Used to read image data (multiple formats support) #endif +#if defined(SUPPORT_IMAGE_EXPORT) + #define STB_IMAGE_WRITE_IMPLEMENTATION + #include "external/stb_image_write.h" // Required for: stbi_write_*() +#endif + #if defined(SUPPORT_IMAGE_MANIPULATION) #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() @@ -140,6 +148,7 @@ static Image LoadPKM(const char *fileName); // Load PKM file #endif #if defined(SUPPORT_FILEFORMAT_KTX) static Image LoadKTX(const char *fileName); // Load KTX file +static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file #endif #if defined(SUPPORT_FILEFORMAT_PVR) static Image LoadPVR(const char *fileName); // Load PVR file @@ -706,15 +715,33 @@ void UpdateTexture(Texture2D texture, const void *pixels) rlUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); } -// Export image as a PNG file -void ExportImage(const char *fileName, Image image) +// Export image data to file +// NOTE: File format depends on fileName extension +void ExportImage(Image image, const char *fileName) { + int success = 0; + // NOTE: Getting Color array as RGBA unsigned char values unsigned char *imgData = (unsigned char *)GetImageData(image); - // NOTE: SavePNG() not supported by some platforms: PLATFORM_WEB, PLATFORM_ANDROID - SavePNG(fileName, imgData, image.width, image.height, 4); - + if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4); + else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData); + else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData); + else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100 + else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName); + else if (IsFileExtension(fileName, ".raw")) + { + // Export raw pixel data (without header) + // NOTE: It's up to the user to track image parameters + FILE *rawFile = fopen(fileName, "wb"); + fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); + fclose(rawFile); + } + else if (IsFileExtension(fileName, ".h")) { } // TODO: Export pixel data as an array of bytes + + if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName); + else TraceLog(LOG_WARNING, "Image could not be exported."); + free(imgData); } @@ -1051,16 +1078,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 +1243,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 @@ -1418,6 +1445,57 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) } } +// Extract color palette from image to maximum size +// NOTE: Memory allocated should be freed manually! +Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount) +{ + #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a)) + + Color *pixels = GetImageData(image); + Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color)); + + int palCount = 0; + for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK + + for (int i = 0; i < image.width*image.height; i++) + { + if (pixels[i].a > 0) + { + bool colorInPalette = false; + + // Check if the color is already on palette + for (int j = 0; j < maxPaletteSize; j++) + { + if (COLOR_EQUAL(pixels[i], palette[j])) + { + colorInPalette = true; + break; + } + } + + // Store color if not on the palette + if (!colorInPalette) + { + palette[palCount] = pixels[i]; // Add pixels[i] to palette + palCount++; + + // We reached the limit of colors supported by palette + if (palCount >= maxPaletteSize) + { + i = image.width*image.height; // Finish palette get + printf("WARNING: Image palette is greater than %i colors!\n", maxPaletteSize); + } + } + } + } + + free(pixels); + + *extractCount = palCount; + + return palette; +} + // Draw an image (source) within an image (destination) // TODO: Feel this function could be simplified... void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) @@ -1450,7 +1528,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 +1608,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 +1624,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); @@ -1555,7 +1633,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Define ImageFont struct? or include Image spritefont in Font struct? Image imFont = GetTextureData(font.texture); - ImageColorTint(&imFont, tint); // Apply color tint to font + ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Make sure image format could be properly colored! + + ImageColorTint(&imFont, tint); // Apply color tint to font // Create image to store text Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); @@ -1588,12 +1668,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 +1696,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 +1717,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 +1839,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 +2091,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 +2190,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 +2201,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 +2214,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 +2359,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); @@ -2330,6 +2411,202 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V } } +void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint) +{ + if (texture.id > 0) + { + float width = (float)texture.width; + float height = (float)texture.height; + + float patchWidth = (destRec.width <= 0.0f)? 0.0f : destRec.width; + float patchHeight = (destRec.height <= 0.0f)? 0.0f : destRec.height; + + if (nPatchInfo.sourceRec.width < 0) nPatchInfo.sourceRec.x -= nPatchInfo.sourceRec.width; + if (nPatchInfo.sourceRec.height < 0) nPatchInfo.sourceRec.y -= nPatchInfo.sourceRec.height; + if (nPatchInfo.type == NPT_3PATCH_HORIZONTAL) patchHeight = nPatchInfo.sourceRec.height; + if (nPatchInfo.type == NPT_3PATCH_VERTICAL) patchWidth = nPatchInfo.sourceRec.width; + + bool drawCenter = true; + bool drawMiddle = true; + float leftBorder = (float)nPatchInfo.left; + float topBorder = (float)nPatchInfo.top; + float rightBorder = (float)nPatchInfo.right; + float bottomBorder = (float)nPatchInfo.bottom; + + // adjust the lateral (left and right) border widths in case patchWidth < texture.width + if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.type != NPT_3PATCH_VERTICAL) + { + drawCenter = false; + leftBorder = (leftBorder / (leftBorder + rightBorder)) * patchWidth; + rightBorder = patchWidth - leftBorder; + } + // adjust the lateral (top and bottom) border heights in case patchHeight < texture.height + if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.type != NPT_3PATCH_HORIZONTAL) + { + drawMiddle = false; + topBorder = (topBorder / (topBorder + bottomBorder)) * patchHeight; + bottomBorder = patchHeight - topBorder; + } + + Vector2 vertA, vertB, vertC, vertD; + vertA.x = 0.0f; // outer left + vertA.y = 0.0f; // outer top + vertB.x = leftBorder; // inner left + vertB.y = topBorder; // inner top + vertC.x = patchWidth - rightBorder; // inner right + vertC.y = patchHeight - bottomBorder; // inner bottom + vertD.x = patchWidth; // outer right + vertD.y = patchHeight; // outer bottom + + Vector2 coordA, coordB, coordC, coordD; + coordA.x = nPatchInfo.sourceRec.x / width; + coordA.y = nPatchInfo.sourceRec.y / height; + coordB.x = (nPatchInfo.sourceRec.x + leftBorder) / width; + coordB.y = (nPatchInfo.sourceRec.y + topBorder) / height; + coordC.x = (nPatchInfo.sourceRec.x + nPatchInfo.sourceRec.width - rightBorder) / width; + coordC.y = (nPatchInfo.sourceRec.y + nPatchInfo.sourceRec.height - bottomBorder) / height; + coordD.x = (nPatchInfo.sourceRec.x + nPatchInfo.sourceRec.width) / width; + coordD.y = (nPatchInfo.sourceRec.y + nPatchInfo.sourceRec.height) / height; + + rlEnableTexture(texture.id); + + rlPushMatrix(); + rlTranslatef(destRec.x, destRec.y, 0); + rlRotatef(rotation, 0, 0, 1); + rlTranslatef(-origin.x, -origin.y, 0); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + if (nPatchInfo.type == NPT_9PATCH) + { + // ------------------------------------------------------------ + // TOP-LEFT QUAD + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // TOP-CENTER QUAD + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad + } + // TOP-RIGHT QUAD + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad + if (drawMiddle) + { + // ------------------------------------------------------------ + // MIDDLE-LEFT QUAD + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad + if (drawCenter) + { + // MIDDLE-CENTER QUAD + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-left corner for texture and quad + } + + // MIDDLE-RIGHT QUAD + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-left corner for texture and quad + } + + // ------------------------------------------------------------ + // BOTTOM-LEFT QUAD + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad + if (drawCenter) + { + // BOTTOM-CENTER QUAD + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-left corner for texture and quad + } + + // BOTTOM-RIGHT QUAD + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-left corner for texture and quad + } + else if (nPatchInfo.type == NPT_3PATCH_VERTICAL) + { + // TOP QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // MIDDLE QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad + } + // BOTTOM QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad + } + else if (nPatchInfo.type == NPT_3PATCH_HORIZONTAL) + { + // LEFT QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // CENTER QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad + } + // RIGHT QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad + } + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); + + } +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2503,7 +2780,7 @@ static Image LoadDDS(const char *fileName) TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); - image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(size*sizeof(unsigned char)); fread(image.data, size, 1, ddsFile); @@ -2600,7 +2877,7 @@ static Image LoadPKM(const char *fileName) int size = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(size*sizeof(unsigned char)); fread(image.data, size, 1, pkmFile); @@ -2630,7 +2907,11 @@ static Image LoadKTX(const char *fileName) // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 // KTX file Header (64 bytes) - // https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ + + // TODO: Support KTX 2.2 specs! + typedef struct { char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 @@ -2684,13 +2965,13 @@ 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; fread(&dataSize, sizeof(unsigned int), 1, ktxFile); - image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); fread(image.data, dataSize, 1, ktxFile); @@ -2704,6 +2985,93 @@ static Image LoadKTX(const char *fileName) return image; } + +// Save image data as KTX file +// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) +static void SaveKTX(Image image, const char *fileName) +{ + // KTX file Header (64 bytes) + // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation + + typedef struct { + char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" + unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 + unsigned int glType; // For compressed textures, glType must equal 0 + unsigned int glTypeSize; // For compressed texture data, usually 1 + unsigned int glFormat; // For compressed textures is 0 + unsigned int glInternalFormat; // Compressed internal format + unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat + unsigned int width; // Texture image width in pixels + unsigned int height; // Texture image height in pixels + unsigned int depth; // For 2D textures is 0 + unsigned int elements; // Number of array elements, usually 0 + unsigned int faces; // Cubemap faces, for no-cubemap = 1 + unsigned int mipmapLevels; // Non-mipmapped textures = 1 + unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0 + // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)... + // KTX 2.0 defines additional header elements... + } KTXHeader; + + // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize + + FILE *ktxFile = fopen(fileName, "wb"); + + if (ktxFile == NULL) TraceLog(LOG_WARNING, "[%s] KTX image file could not be created", fileName); + else + { + KTXHeader ktxHeader; + + // KTX identifier (v2.2) + //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; + //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; + + // Get the image header + strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature + ktxHeader.endianness = 0; + ktxHeader.glType = 0; // Obtained from image.format + ktxHeader.glTypeSize = 1; + ktxHeader.glFormat = 0; // Obtained from image.format + ktxHeader.glInternalFormat = 0; // Obtained from image.format + ktxHeader.glBaseInternalFormat = 0; + ktxHeader.width = image.width; + ktxHeader.height = image.height; + ktxHeader.depth = 0; + ktxHeader.elements = 0; + ktxHeader.faces = 1; + ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) + ktxHeader.keyValueDataSize = 0; // No extra data after the header + + rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function + ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only + + // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC + + if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export."); + else + { + fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile); + + int width = image.width; + int height = image.height; + int dataOffset = 0; + + // Save all mipmaps data + for (int i = 0; i < image.mipmaps; i++) + { + unsigned int dataSize = GetPixelDataSize(width, height, image.format); + fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile); + fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile); + + width /= 2; + height /= 2; + dataOffset += dataSize; + } + } + + fclose(ktxFile); // Close file pointer + } +} #endif #if defined(SUPPORT_FILEFORMAT_PVR) @@ -2845,7 +3213,7 @@ static Image LoadPVR(const char *fileName) } int dataSize = image.width*image.height*bpp/8; // Total data size in bytes - image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); + image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char)); // Read data from file fread(image.data, dataSize, 1, pvrFile); diff --git a/src/utils.c b/src/utils.c index f7c19afb4..b2951040c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,21 +4,10 @@ * * CONFIGURATION: * -* #define SUPPORT_SAVE_PNG (defined by default) -* Support saving image data as PNG fileformat -* NOTE: Requires stb_image_write library -* -* #define SUPPORT_SAVE_BMP -* Support saving image data as BMP fileformat -* NOTE: Requires stb_image_write library -* * #define SUPPORT_TRACELOG * Show TraceLog() output messages * NOTE: By default LOG_DEBUG traces not shown * -* DEPENDENCIES: -* stb_image_write - BMP/PNG writting functions -* * * LICENSE: zlib/libpng * @@ -62,12 +51,6 @@ FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) - #define STB_IMAGE_WRITE_IMPLEMENTATION - #include "external/stb_image_write.h" // Required for: stbi_write_bmp(), stbi_write_png() -#endif - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -160,24 +143,6 @@ void TraceLog(int msgType, const char *text, ...) #endif // SUPPORT_TRACELOG } -// Creates a BMP image file from an array of pixel data -void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize) -{ -#if defined(SUPPORT_SAVE_BMP) && (defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)) - stbi_write_bmp(fileName, width, height, compSize, imgData); - TraceLog(LOG_INFO, "BMP Image saved: %s", fileName); -#endif -} - -// Creates a PNG image file from an array of pixel data -void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize) -{ -#if defined(SUPPORT_SAVE_PNG) && (defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)) - stbi_write_png(fileName, width, height, compSize, imgData, width*compSize); - TraceLog(LOG_INFO, "PNG Image saved: %s", fileName); -#endif -} - // Keep track of memory allocated // NOTE: mallocType defines the type of data allocated /* diff --git a/src/utils.h b/src/utils.h index dfdb0c2ae..08b33962f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,10 +32,6 @@ #include // Required for: AAssetManager #endif -#ifndef SUPPORT_SAVE_PNG -#define SUPPORT_SAVE_PNG 1 -#endif - //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- @@ -58,13 +54,6 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -#if defined(SUPPORT_SAVE_BMP) -void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize); -#endif -#if defined(SUPPORT_SAVE_PNG) -void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize); -#endif - #if defined(PLATFORM_ANDROID) void InitAssetManager(AAssetManager *manager); // Initialize asset manager from android app FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() diff --git a/templates/advance_game/resources/mecha.png b/templates/advance_game/resources/mecha.png index 8022d18c4..9213fa2d1 100644 Binary files a/templates/advance_game/resources/mecha.png and b/templates/advance_game/resources/mecha.png differ